Authentication

Betakom partner endpoints use HMAC-SHA256 request signing — the same scheme Stripe uses for webhooks. You never send your secret over the wire; instead you sign each request with it and Betakom recomputes the signature to verify it. Your partnerId and secret are issued during onboarding (see Get Started).

Umgebung
https://api.betakom.de

Request headers

Every signed request carries these three headers:

FieldTypeRequiredDescription
x-partner-idstringrequiredYour public partner identifier (e.g. "acme"). Selects the credential — and therefore the environment — used to verify the signature.
x-betakom-timestampstringrequiredUnix seconds at signing time. Must be within ±5 minutes of Betakom server time, otherwise the request is rejected (replay protection).
x-betakom-signaturestringrequiredv1=<hex>, where <hex> = HMAC-SHA256(secret, "<timestamp>.<rawBody>"). The bare <hex> form (without the v1= prefix) is also accepted.

How to sign a request

  1. Serialize the request body to a string (payload). You must sign and send the exact same bytes — re-serializing JSON later will break the signature.
  2. Take the current Unix time in seconds (timestamp).
  3. Compute signature = HMAC-SHA256(secret, "<timestamp>.<payload>") and hex-encode it.
  4. Send x-partner-id, x-betakom-timestamp and x-betakom-signature: v1=<hex>.
Node.js
import crypto from 'crypto';

const PARTNER_ID = 'acme';
const SECRET = process.env.BETAKOM_PARTNER_SECRET; // shared with Betakom

async function ingestOrder(body) {
  const payload = JSON.stringify(body);
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const signed = `${timestamp}.${payload}`;
  const signature = 'v1=' + crypto
    .createHmac('sha256', SECRET)
    .update(signed)
    .digest('hex');

  return fetch('https://api.betakom.de/api/v1/partners/orders/ingest', {
    method: 'POST',
    headers: {
      'content-type':        'application/json',
      'x-partner-id':        PARTNER_ID,
      'x-betakom-timestamp': timestamp,
      'x-betakom-signature': signature,
    },
    body: payload, // sign and send the EXACT same bytes
  });
}
cURL
# 1. Build the body once and sign "<timestamp>.<body>"
TS=$(date +%s)
BODY='{"partnerOrderId":"PO-1001","customer":{"email":"kunde@example.com"},"loading":{"country":"DE","city":"Berlin","postalCode":"10115"},"unloading":{"country":"DE","city":"Muenchen","postalCode":"80331"},"vehicleType":"sprinter","totalPriceEur":480}'
SIG=$(printf '%s' "$TS.$BODY" | openssl dgst -sha256 -hmac "$BETAKOM_PARTNER_SECRET" | awk '{print $2}')

# 2. Send the same bytes you signed
curl -X POST 'https://api.betakom.de/api/v1/partners/orders/ingest' \
  -H 'content-type: application/json' \
  -H "x-partner-id: acme" \
  -H "x-betakom-timestamp: $TS" \
  -H "x-betakom-signature: v1=$SIG" \
  -d "$BODY"

Verification & errors

  • Replay protection: the timestamp must be within ±5 minutes of Betakom server time, or the request is rejected with 401.
  • Signature mismatch / unknown partner: returns 401 with a generic message (we don't reveal whether a partner exists or is inactive).
  • Body limits: the order-ingest endpoint caps bodies at 100 KB (413) and rate-limits to 60 requests/minute per partner (429).

Other authentication schemes

The Carrier Integration API uses a per-connection token in the URL plus an x-betakom-signature over the raw body. The Tracking API uses a signed per-order share token (the t query parameter). The Pricing API (beta) currently accepts a shared API key in the request body. See each product's reference page for details.