Browse documentation

Type to search the documentation. Press Esc to close.

What do Scribble API errors look like?

For developers

Every error is the same shape: { "error": { "code", "message", "docs" } }. The code is stable and safe to switch on; the message is written for a person reading a terminal and may change. A 4xx means nothing was created and nothing was charged.

On this page
The error envelope
{
  "error": {
    "code": "invalid_recipient",
    "message": "recipient.line1, recipient.state, recipient.zip are required. A card cannot be written for an address we cannot address an envelope to.",
    "docs": "https://scribblecards.com/app/integrations"
  }
}

#Every error code

Scribble API error codes by status
StatusCodeMeaningCharged?
400invalid_jsonThe body is not valid JSON.No
400invalid_bodyThe body is valid JSON but not an object.No
400missing_idNo card code in the path.No
401missing_keyNo bearer token.No
401malformed_keyThe token is not a Scribble key.No
401invalid_keyUnknown key.No
401revoked_keyThe key was revoked.No
401invalid_hookThe inbound hook URL is not recognised.No
403missing_scopeThe key lacks the scope this endpoint needs.No
403hook_disabledThe inbound hook was switched off.No
404card_not_foundNo card with that code belongs to this organisation.No
404design_not_foundNo design with that id belongs to this organisation.No
402no_creditsThe balance is empty, or ran out between the check and the spend. Nothing was created.No
415unsupported_media_typeSend Content-Type: application/json.No
422invalid_recipientA required address field is missing or malformed.No
422missing_messagemessage was empty.No
422message_too_longmessage exceeds 500 characters.No
422invalid_datearrive_on is not YYYY-MM-DD.No
422missing_nameA contact needs a name.No
422invalid_birthdaybirthday is not MM-DD or YYYY-MM-DD.No
422template_incompleteAn inbound hook's message template needs a field the payload has no value for.No
429rate_limitedSee rate limits.No
500read_failedA read failed on our side.No
500create_failedA contact could not be created.No
500update_failedA contact could not be updated.No
500order_failedThe card could not be created. Nothing was charged.No
500production_failedThe card was accepted but could not be prepared for production. Nothing was charged.No
500internal_errorSomething went wrong on our side.No

#Retrying safely

When it is safe to retry
StatusSafe to retry?Why
4xxYes, after fixing the requestNothing was created.
429Yes, after Retry-After secondsThe request never reached the handler.
500 with order_failed, production_failed, no_creditsYesThese explicitly state nothing was charged.
500 with internal_errorCheck firstThe failure happened after the handler started. Call GET /api/v1/cards and look for the recipient before retrying.
A timeout or a dropped connectionCheck firstYou do not know whether the card was created. Look before you retry.
A retry that will not double-send
async function sendCard(event, body) {
  // The guard lives in YOUR system, because Scribble has no
  // idempotency key. Write the claim before the call.
  if (await alreadySent(event.id)) return;
  await claim(event.id);

  const res = await fetch("https://scribblecards.com/api/v1/cards", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SCRIBBLE_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  if (res.status === 429) {
    const wait = Number(res.headers.get("retry-after") ?? 60);
    await releaseClaim(event.id);
    throw new RetryAfter(wait);
  }

  if (!res.ok) {
    const { error } = await res.json();
    await releaseClaim(event.id);          // nothing was charged on a 4xx
    throw new Error(`${error.code}: ${error.message}`);
  }

  const card = await res.json();
  await recordSent(event.id, card.id);     // e.g. "SC-NH4DERB"
  return card;
}

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