📡 SMS-server — API

Skicka och ta emot SMS via SMS-servern. Du autentiserar varje anrop med din API-nyckel i headern X-API-Key. Nyckeln (och din webhook_secret för att verifiera inkommande anrop) fÄr du av administratören.

Bas-URL: https://utskick.karlberg.nu

1. Skicka SMS

POST /api/send — ett SMS direkt

Skickar omedelbart och svarar nÀr SMS:et Àr skickat (eller misslyckats).

curl -s https://utskick.karlberg.nu/api/send \
  -H "X-API-Key: DIN_NYCKEL" \
  -H "Content-Type: application/json" \
  -d '{"number":"+46701234567","message":"Hej frÄn oss!"}'

# Svar:
# {"id": 42, "status": "sent", "router_ip": "10.0.0.3", "attempts": 1}

POST /api/send-bulk — flera mottagare

Köas och skickas med jĂ€mna, oregelbundna mellanrum (skonsamt mot operatören). Du fĂ„r ett id per meddelande — leveransstatus kommer via webhook eller pollning.

curl -s https://utskick.karlberg.nu/api/send-bulk \
  -H "X-API-Key: DIN_NYCKEL" \
  -H "Content-Type: application/json" \
  -d '{"numbers":["+46701234567","+46707654321"],"message":"Kampanjtext"}'

# Svar:
# {"ids":[43,44], "count":2, "status":"queued", "queue":2}

Valfria fÀlt (bÄda endpoints)

Servern klÀr normalt meddelandet med en varierad hÀlsning + ditt tjÀnstnamn, och skriver om kod-meddelanden till OTP xxxx-format (sÄ mobilen kÀnner igen engÄngskoden). Du kan styra detta:

2. Ta emot leveransrapporter och svar (webhook)

Ge administratören en webhook-URL (din egen endpoint). Dit POST:ar SMS-servern tvÄ sorters hÀndelser, som JSON, signerade sÄ att du kan verifiera att de kommer frÄn oss. Du behöver alltsÄ bygga en mottagande endpoint pÄ din sida.

Leveransrapport

POST:as nÀr ett av dina utskick nÄr slutstatus (sent eller failed).

{
  "type": "delivery_report",
  "id": 43,
  "number": "+46701234567",
  "status": "sent",          // sent | failed
  "router_ip": "10.0.0.3",
  "attempts": 1,
  "error": null
}

Inkommande svar

NÀr nÄgon svarar pÄ ett SMS du skickat POST:as svaret till dig (svaret kopplas till dig eftersom du var den som senast skrev till numret).

{
  "type": "inbound_sms",
  "id": 87,
  "from": "+46701234567",
  "message": "Ja tack!",
  "date": "2026-06-04 21:15:00"
}

Verifiera signaturen

Varje webhook-anrop har headern X-SMS-Signature = base64(HMAC_SHA256(rÄ_body, webhook_secret)). RÀkna ut samma vÀrde pÄ din sida och jÀmför. Verifiera mot den rÄa bodyn (inte en om-serialiserad JSON).

Node.js (Express):

const crypto = require("crypto");
const SECRET = "DIN_WEBHOOK_SECRET";

// Viktigt: lĂ€s den RÅA bodyn — t.ex. express.raw({ type: "application/json" })
app.post("/sms-webhook", express.raw({ type: "*/*" }), (req, res) => {
  const sig = crypto.createHmac("sha256", SECRET).update(req.body).digest("base64");
  if (sig !== req.get("X-SMS-Signature")) return res.sendStatus(401);

  const event = JSON.parse(req.body.toString());
  if (event.type === "delivery_report") { /* ... */ }
  if (event.type === "inbound_sms")     { /* ... svara kunden via /api/send ... */ }
  res.sendStatus(200);
});

Python (Flask):

import base64, hashlib, hmac
from flask import Flask, request, abort
SECRET = b"DIN_WEBHOOK_SECRET"
app = Flask(__name__)

@app.post("/sms-webhook")
def hook():
    raw = request.get_data()                      # rÄ body
    sig = base64.b64encode(hmac.new(SECRET, raw, hashlib.sha256).digest()).decode()
    if not hmac.compare_digest(sig, request.headers.get("X-SMS-Signature", "")):
        abort(401)
    event = request.get_json()
    # event["type"] == "delivery_report" | "inbound_sms"
    return "", 200
💬 TvĂ„vĂ€gs-konversation: svara pĂ„ ett inkommande inbound_sms genom att helt enkelt skicka ett nytt /api/send till samma from-nummer. DĂ„ fortsĂ€tter trĂ„den och nĂ€sta svar kommer tillbaka till dig igen.

3. HĂ€mta status utan webhook (pollning)

Vill du inte bygga en webhook kan du i stÀllet hÀmta lÀget med din nyckel:

EndpointBeskrivning
GET /api/my/infoDin nyckels uppgifter, antal skickade och din webhook_secret.
GET /api/my/messages?limit=50Dina utgÄende SMS med leveransstatus (sent/failed/pending).
GET /api/my/received?limit=50Inkommande svar kopplade till dig.
curl -s "https://utskick.karlberg.nu/api/my/messages?limit=20" -H "X-API-Key: DIN_NYCKEL"

Fel & grÀnser

KodBetyder
401Saknad/ogiltig/inaktiverad API-nyckel.
402MĂ„nadskvoten för din nyckel Ă€r nĂ„dd — kontakta administratören.
429HastighetsgrĂ€ns nĂ„dd (för mĂ„nga SMS per minut) — vĂ€nta och försök igen.
503Inga routrar online just nu — försök igen senare.
502Utskicket misslyckades pÄ alla routrar.

Din nyckels pris/SMS, ev. mÄnadstak och förbrukning denna mÄnad ser du via GET /api/my/info (fÀlten price_ore, monthly_cap, month_count, month_cost_ore).

Externa nycklar fĂ„r bara skicka och hĂ€mta sina egna data — inte se andras utskick, kontakter eller dashboarden.