Browse documentation

Type to search the documentation. Press Esc to close.

Scribble cards API: send and look up a handwritten card

For developers

POST /api/v1/cards sends one handwritten card and spends one credit. GET /api/v1/cards lists what you have sent, and GET /api/v1/cards/{card_code} returns one card with its per-step production progress and its arrival window.

On this page

#POST/api/v1/cards

Send one handwritten card. Spends one credit.

Requires scope cards:send

Body parameters

FieldTypeDescription
recipientrequiredobjectThe postal address. See the sub-fields below.
recipient.namerequiredstringWho the envelope is addressed to.
recipient.line1requiredstringStreet line.
recipient.line2stringApartment, suite, floor or unit.
recipient.cityrequiredstringCity.
recipient.staterequiredstringTwo-letter US state code. Upper-cased for you.
recipient.ziprequiredstringFive digits, optionally with a +4.
recipient.countrystringDefaults to US. Scribble mails inside the US only.
messagerequiredstringWhat gets written on the card, in ink. Send the finished text with merge fields already substituted. Maximum 500 characters; line breaks are honoured. Aim for 250–400 — 500 is the ceiling, not a target, and a long name can push a message that fits in testing over the edge in production.
handwritingstringA handwriting slug. An unrecognised value falls back to your organisation's default hand without an error.
design_idstringThe id of one of your saved designs. Its artwork and inside layout are used. An unknown id returns 404 design_not_found.
arrive_onstringYYYY-MM-DD. Production is planned backwards from it and the arrival window collapses to that single day. Leave it out for the soonest date we can honour.
referencestringFree text, echoed back in the response so you can correlate without storing our ids. It does not deduplicate. Truncated to 40 characters.
Send a card
curl -X POST https://scribblecards.com/api/v1/cards \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": {
      "name": "Priya Raman",
      "line1": "1 Fifth Avenue",
      "line2": "Apt 12B",
      "city": "New York",
      "state": "NY",
      "zip": "10003"
    },
    "message": "Congratulations on the new role, Priya.\n\nEveryone at Northwind",
    "reference": "crm-deal-8812"
  }'
201 Created
{
  "id": "SC-NH4DERB",
  "object": "card",
  "status": "scheduled",
  "held_reason": null,
  "recipient": { "name": "Priya Raman" },
  "arrives_between": {
    "earliest": "2026-08-12",
    "latest": "2026-08-14"
  },
  "reference": "crm-deal-8812",
  "note": "One credit was charged. Arrival is a window: we mark a card delivered only on a carrier scan.",
  "url": "https://scribblecards.com/app/orders/SC-NH4DERB"
}
  • status is scheduled when the card is going to be made, or held when something needs a person. A held card is not charged, and held_reason says why in plain words.
  • arrives_between is a window, never a promised date. With no arrive_on, it spans three calendar days ending on the soonest achievable arrival. With arrive_on, earliest and latest are the same day.
  • id is the card code. It looks like SC-NH4DERB: the prefix SC-, six payload characters and a check character, in an alphabet with no I, L, O or U so nothing can be misread aloud.

#GET/api/v1/cards

List the cards this organisation has sent, newest first.

Requires scope cards:read

Query parameters

ParameterTypeDescription
limitintegerDefault 50, maximum 200. Anything larger is clamped.
statusstringFilter by production status. See the status table below.
List the two most recent cards
curl "https://scribblecards.com/api/v1/cards?limit=2" \
  -H "Authorization: Bearer sk_live_..."
200 OK
{
  "object": "list",
  "has_more": true,
  "data": [
    {
      "id": "SC-2APXR1Z",
      "object": "card",
      "status": "in_production",
      "recipient": { "name": "Tomas Nowak", "city": "Boston", "state": "MA" },
      "arrives_between": { "earliest": "2026-08-12", "latest": "2026-08-14" },
      "mailed_at": null,
      "delivered_at": null,
      "created_at": "2026-08-05T06:48:57.080629+00:00"
    },
    {
      "id": "SC-5DKF1SW",
      "object": "card",
      "status": "written",
      "recipient": { "name": "Aisha Bello", "city": "Austin", "state": "TX" },
      "arrives_between": { "earliest": "2026-08-12", "latest": "2026-08-14" },
      "mailed_at": null,
      "delivered_at": null,
      "created_at": "2026-08-05T06:19:04.555985+00:00"
    }
  ]
}
  • has_more is true when the page came back full. There is no cursor: page by narrowing status or raising limit.
  • reference is not returned by this endpoint, so you cannot look a card up by your own identifier. Store the card code we return on the POST.

#GET/api/v1/cards/{card_code}

One card: status, per-step production progress, arrival window.

Requires scope cards:read

Look up one card
curl https://scribblecards.com/api/v1/cards/SC-NH4DERB \
  -H "Authorization: Bearer sk_live_..."
200 OK
{
  "id": "SC-NH4DERB",
  "object": "card",
  "status": "pending",
  "held_reason": null,
  "recipient": {
    "name": "Priya Raman",
    "line1": "1 Fifth Avenue",
    "line2": "Apt 12B",
    "city": "New York",
    "state": "NY",
    "zip": "10003",
    "country": "US"
  },
  "handwriting": "PremiumUltra28",
  "arrives_between": {
    "earliest": "2026-08-12",
    "latest": "2026-08-14"
  },
  "steps": [
    { "step": "card_stock_prepared", "status": "pending", "completed_at": null },
    { "step": "card_written",        "status": "pending", "completed_at": null },
    { "step": "envelope_addressed",  "status": "pending", "completed_at": null },
    { "step": "packed",              "status": "pending", "completed_at": null },
    { "step": "stamped",             "status": "pending", "completed_at": null },
    { "step": "posted",              "status": "pending", "completed_at": null }
  ],
  "mailed_at": null,
  "delivered_at": null,
  "created_at": "2026-08-05T09:37:33.549357+00:00",
  "url": "https://scribblecards.com/app/orders/SC-NH4DERB"
}
  • Card codes are case-insensitive on lookup and are normalised, so sc-nh4derb resolves to the same card.
  • A card belonging to another organisation returns 404, not 403. Existence is not leaked across tenants.
  • steps always contains all six entries in production order. A step that has not started is pending; a retried step reports its current state rather than the failure it recovered from.

#Card statuses

Statuses returned by the GET endpoints
StatusMeaning
pendingCreated and scheduled. Nothing physical has happened yet.
queuedReleased to the floor for a production run.
in_productionSomewhere in the six steps.
writtenThe pen has finished the card and the envelope.
packedIn its envelope, stamped, in a tray.
postedHanded to USPS. mailed_at is set.
deliveredA carrier scan confirmed delivery. delivered_at is set.
blockedHeld. Needs a person. held_reason says why. Not charged.
cancelledStopped before production.
failedCould not be produced.

#Common questions

How do I know when a card has been mailed?
Poll this endpoint and watch mailed_at. The card.mailed webhook event is defined but is not emitted yet, so a subscription to it will never fire. Poll the cards you care about on a sensible interval rather than every card every hour.
Can I cancel a card sent through the API?
Not through the API. There is no delete or cancel endpoint. Cancel from Sent cards in the app while the card is still unwritten.
Can I send the same card to several recipients in one request?
No. One request, one card. For a batch, either loop within the rate limit or build a campaign.
Is there a test mode?
No sandbox and no test keys. Develop against an account with a deliberately small balance.

Last checked against the product on . Something wrong or missing? Tell us.