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
| Field | Type | Description |
|---|---|---|
recipientrequired | object | The postal address. See the sub-fields below. |
recipient.namerequired | string | Who the envelope is addressed to. |
recipient.line1required | string | Street line. |
recipient.line2 | string | Apartment, suite, floor or unit. |
recipient.cityrequired | string | City. |
recipient.staterequired | string | Two-letter US state code. Upper-cased for you. |
recipient.ziprequired | string | Five digits, optionally with a +4. |
recipient.country | string | Defaults to US. Scribble mails inside the US only. |
messagerequired | string | What 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. |
handwriting | string | A handwriting slug. An unrecognised value falls back to your organisation's default hand without an error. |
design_id | string | The id of one of your saved designs. Its artwork and inside layout are used. An unknown id returns 404 design_not_found. |
arrive_on | string | YYYY-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. |
reference | string | Free text, echoed back in the response so you can correlate without storing our ids. It does not deduplicate. Truncated to 40 characters. |
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"
}'{
"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"
}statusisscheduledwhen the card is going to be made, orheldwhen something needs a person. A held card is not charged, andheld_reasonsays why in plain words.arrives_betweenis a window, never a promised date. With noarrive_on, it spans three calendar days ending on the soonest achievable arrival. Witharrive_on,earliestandlatestare the same day.idis the card code. It looks likeSC-NH4DERB: the prefixSC-, 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
| Parameter | Type | Description |
|---|---|---|
limit | integer | Default 50, maximum 200. Anything larger is clamped. |
status | string | Filter by production status. See the status table below. |
curl "https://scribblecards.com/api/v1/cards?limit=2" \
-H "Authorization: Bearer sk_live_..."{
"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_moreis true when the page came back full. There is no cursor: page by narrowingstatusor raisinglimit.referenceis 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
curl https://scribblecards.com/api/v1/cards/SC-NH4DERB \
-H "Authorization: Bearer sk_live_..."{
"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-nh4derbresolves to the same card. - A card belonging to another organisation returns
404, not403. Existence is not leaked across tenants. stepsalways contains all six entries in production order. A step that has not started ispending; a retried step reports its current state rather than the failure it recovered from.
#Card statuses
| Status | Meaning |
|---|---|
pending | Created and scheduled. Nothing physical has happened yet. |
queued | Released to the floor for a production run. |
in_production | Somewhere in the six steps. |
written | The pen has finished the card and the envelope. |
packed | In its envelope, stamped, in a tray. |
posted | Handed to USPS. mailed_at is set. |
delivered | A carrier scan confirmed delivery. delivered_at is set. |
blocked | Held. Needs a person. held_reason says why. Not charged. |
cancelled | Stopped before production. |
failed | Could not be produced. |
#Common questions
- How do I know when a card has been mailed?
- Poll this endpoint and watch
mailed_at. Thecard.mailedwebhook 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.