Hopp til innhold
skribbskribb

Utviklere

Webhooks

Alle sidene

Oppsett

Du legger inn en adresse under Innstillinger, fanen API, huker av for hendelsene du bryr deg om og får en signeringsnøkkel du bruker til å sjekke at forespørselen kom fra skribb.

Adressen må være https og nås utenfra. Vi sender ingenting til en adresse på ditt eget nett eller tilbake til skribb, og vi sjekker det både når du legger den inn og hver gang vi sender.

Hendelser

  • order.paidNy betalt bestilling
  • order.fulfilledBestilling merket som sendt
  • order.refundedBestilling refundert
  • stock.changedLagerbeholdning endret
  • stock.lowSnart tomt for en vare
  • subscriber.confirmedNy bekreftet abonnent
  • subscriber.unsubscribedAbonnent meldte seg av
  • post.publishedInnlegg publisert
  • broadcast.sentUtsending gikk ut
  • broadcast.failedUtsending kom ikke fram

Lagersynk begge veier

stock.changed sier hvor en beholdning står nå, og hva den sto på før. Én hendelse per vare: en bestilling på tre varelinjer gir tre, og en import som fyller på førti varer gir førti, uansett hvor mange bevegelser som ligger bak hver av dem.

Andre veien sender du PATCH /v1/products/{id}/stock. Bruk delta og ikke set. Hold lageret i sync →

causes er hva som flyttet beholdningen. Et salg i butikken er stripe, og det du selv har skrevet tilbake er api.

stock.changed
{
  "id": "8c21f0d4-...",
  "event": "stock.changed",
  "created_at": "2026-09-18T09:14:02.000Z",
  "data": {
    "stock": {
      "product_id": "prd_7Qa2Vn",
      "variant_id": "var_1Kd9Zs",
      "title": "Kopp i steintøy",
      "variant_label": "Stor",
      "inventory": 2,
      "previous_inventory": 5,
      "causes": ["stripe"],
      "low": true
    }
  }
}

inventory: null betyr at varen har sluttet å telle beholdning. Det er ikke det samme som null på lager, og en synk som leser det som utsolgt tar feil.

stock.low har samme body, men kommer bare når beholdningen krysser grensen på 3. Den gjentas ikke for hvert salg under grensen, og den kommer igjen først etter at varen har vært over den.

Lagerhendelsene kommer inntil et minutt etter at beholdningen flyttet seg.

Body

Bodyen er JSON. id er unik per utsending. En bestilling kommer i samme form som i /v1/orders, og en utsending som i /v1/broadcasts.

Body
{
  "id": "3f9a1c7e-...",
  "event": "order.paid",
  "created_at": "2026-07-26T18:25:50.000Z",
  "data": {
    "order": { "id": "ord_3Ln9Ra", "status": "paid", "total_ore": 57700, "…": "…" }
  }
}

Sjekk signaturen

Hver forespørsel har Skribb-Signature: t=<sekunder>,v1=<hex>. Signaturen dekker t og bodyen sammen, ikke bodyen alene. Avvis alt som er mer enn fem minutter gammelt.

Regn på den rå bodyen, ikke på noe du har parset og skrevet ut igjen. Et rammeverk som normaliserer JSON-en for deg, endrer bytene, og da stemmer ikke signaturen lenger.

Node
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(parts.t));
  if (!Number.isFinite(age) || age > 300) return false;

  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(parts.v1 ?? "", "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}

Svar og gjentatte forsøk

Svar 2xx så fort du kan, og gjør det tunge arbeidet etterpå. Vi venter i ti sekunder. Alt annet enn 2xx, og et svar som ikke kommer, teller som mislykket.

Da prøver vi igjen etter ett minutt, så fem, så tretti, så to timer, så seks. Fem forsøk over rundt åtte timer. Etter det gir vi opp for den ene utsendingen. Svarer adressen ikke over lengre tid, slår vi den av og sier fra i innstillingene.

Vi lover ikke rekkefølge. To hendelser kan komme i motsatt rekkefølge av at de skjedde, så se på innholdet, ikke på ankomsten. Vi lover heller ikke at noe kommer bare én gang: et nytt forsøk etter et svar som gikk tapt, ser likt ut for oss. Derfor er id der.