Skip to content
gutternote demo

Hit Gutternote, bottom right, then click whatever offends you.

Got a worse site? Join the waitlist →

Hit Gutternote, bottom right, then click whatever offends you.

Join the waitlist →

Documentation

Everything below works against the sandbox with a test key. Swap the key for a live one when you are ready; nothing else changes.

Quickstart

Install the client and send your first event. The call is idempotent on the key you pass, so a retry after a timeout is safe.

npm install @acme/node
import { Acme } from '@acme/node'

const acme = new Acme(process.env.ACME_KEY)

const event = await acme.events.send({
  provider: 'northwind',
  type: 'invoice.paid',
  idempotencyKey: 'in_8821',
  data: { id: 'in_8821', total: 4200 },
})

console.log(event.status) // 'delivered'

Authentication

Keys are scoped to one account, one region and a set of providers. A key beginning ak_test_ only ever reaches the sandbox, so a test suite cannot call a real provider by accident.

curl https://api.acme.example/v1/events \
  -H "Authorization: Bearer $ACME_KEY" \
  -d provider=northwind \
  -d type=invoice.paid

Events

Every provider is normalised onto one envelope. The data object is the provider's own payload, untouched; everything outside it is ours and is the same everywhere.

{
  "id": "evt_01HZX9",
  "type": "invoice.paid",
  "provider": "northwind",
  "created": "2026-02-11T09:41:22Z",
  "attempts": 1,
  "data": { "id": "in_8821", "total": 4200 }
}

Errors

Errors are a status code and a stable string. The string is what to branch on; the prose is for a human reading a log.

StatusCodeMeans
400 invalid_request The payload did not match the event schema.
401 bad_key The key is unknown, revoked, or for another region.
403 scope_denied The key is valid but not scoped to this provider.
409 duplicate_event An event with this idempotency key already landed.
429 rate_limited Slow down. The Retry-After header says by how much.