veil

Read a card’s email notices

An active key of the card reads its opt-in to email notices, with a signed GET.

GET/v1/identities/{n}/notices
Access
Signed request
Client
Local or server client

Parameters#

NameLocationMeaning
nRequiredPathCard number.

Send the common signed-request headers, including X-Nonce and X-Timestamp. A signed GET has no JSON body. Request signing

Request examples#

cURL
curl -fsS \
  -H "X-Public-Key: $PUBLIC_KEY" -H "X-Signature: $REQUEST_SIGNATURE" \
  -H "X-Nonce: $NONCE" -H "X-Timestamp: $TIMESTAMP_MS" \
  "https://veil.rip/v1/identities/CARD_NUMBER/notices"

Response#

HTTP 200application/json

The card’s opt-in: whether it is on, the language, where the emails go and which events send one.

FieldTypeMeaning
nintegerCard number.
onbooleanWhether the card is opted in.
langstring | nullLanguage of the emails, en or it, when on.
sincestring | nullWhen the owner opted in.
lastSentAtstring | nullWhen the last notice went.
emailstring | nullWhere the emails go: the card’s oldest verified email address, read at sending time. Null without one.
mailbooleanWhether this server can send email at all.
eventsarray<string>The events a notice is sent for: takeover, device_added, device_revoked, backup_missing, proof_failing, key_idle, and canary_due for the owners of the Veil organisation.
dailyCapintegerEmails a card gets at most per UTC day; more notices go as one digest.

Response headers#

HeaderValueMeaning
Content-Typeapplication/jsonUse the declared media type before choosing a parser.
DateHTTP dateServer time. Compare it with your clock when signing requests.
Retry-Afterinteger seconds, on rate_limited or busyWait at least this long before retrying. Other 429/503 codes do not necessarily include it.

Usage notes#

  • Opting in needs a verified email link on the card. The notices go to its oldest verified address, one email per event, in plain text, with the card’s address, why it came and a stop link; at most 3 a card per UTC day, then a digest. Nothing signed changes.

Errors#

API errors return an error object. Use error.code for program logic; error.message is English text for people and can change. Response and error guide

HTTPCodeMeaning and next step
400invalid_parameter

A path, query or body field is missing, malformed or outside its accepted range.

Correct the field named in error.message before sending again.

403key_mismatch

The signing key does not have authority over this card or operation.

Use an active device key with the required owner or member role.

404not_found

The requested card, key, alias, code or resource was not found.

Check the identifier. Temporary codes and previews may already have expired or been used.

422not_for_organizations

Notices are for a person’s card. An organisation has none of its own.

Each owner opts in on their own card.

Shared request errors
HTTPCodeMeaning and next step
400invalid_nonce

The signed-request nonce has an invalid format.

Generate a fresh 16 to 128-character nonce using letters, digits, _ or -.

400invalid_signature_alg

X-Signature-Alg is unsupported.

Use a supported signing algorithm and matching key/signature encoding.

400invalid_timestamp

The signed-request timestamp is not Unix milliseconds.

Use an integer Unix timestamp in milliseconds and sign the corrected request.

401invalid_signature

The HTTP request signature does not verify.

Sign the exact method, path and body bytes, without changing them afterwards.

401missing_signature

The request requires a device signature and its headers are missing or unreadable.

Send X-Public-Key, X-Signature and the signed-request metadata. An API key does not replace a device signature.

401stale_timestamp

The signed timestamp is outside the accepted clock window.

Compare the server Date header with your clock, then generate a fresh nonce, timestamp and signature.

403key_banned

The device signing key is suspended by Veil staff.

Use an authorised active key or ask staff to review the restriction.

409replayed_nonce

The signing key already used this nonce.

Check whether the previous request succeeded before making a fresh signed request.

429rate_limited

An anonymous, signed-request or API-key allowance was exceeded.

Wait at least Retry-After seconds. Share a queue and retry budget across your workers.

500internal_error

An unexpected server error occurred. Internal details are not returned.

Retry reads with bounded backoff. Check write results before sending a new signed operation.

503busy

The application has reached a temporary concurrency or database limit.

Wait Retry-After seconds and retry with bounded backoff.