veil

Declare an account stolen, abandoned or moved

The owner signs an alert about one of the card’s linked accounts. From then on every lookup of that account opens with it, until a regained alert ends it.

POST/v1/identities/{n}/alerts
Access
Signed request
Client
Local or server client

Parameters#

NameLocationMeaning
nRequiredPathPermanent positive card number.

JSON body#

NameLocationMeaning
statementRequiredJSON{platform, since, state, subject, time, to, type: "alert", v: 1}. state is stolen (since a date, YYYY-MM-DD), abandoned, moved (to = {handle, subject}) or regained.
saltRequiredJSON32 random bytes in 64 lowercase hex characters, chosen by the browser.
entryRequiredJSON{text, signature}: the log entry {c, key, kind, signer, time, token, v: 2} in canonical JSON without its signature, and the signature of "veil-rubrica:card-entry:v2\n" + text with X-Public-Key.

This call also needs the common signed-request headers, nonce and timestamp. Prepare and sign request.json before using the example. Request signing

Request examples#

cURL
curl -fsS -X POST \
  -H "X-Public-Key: $PUBLIC_KEY" \
  -H "X-Signature: $REQUEST_SIGNATURE" --json @request.json \
  "https://veil.rip/v1/identities/1/alerts"

Response#

HTTP 201application/json

The alert, its entry and its proof.

FieldTypeMeaning
idintegerThe entry’s position in the log: the statement’s permanent id.
kindstringstatement or alert.
nintegerThe card the statement is by.
signerobjectn and token of the person card whose key signed: the card itself, or an owner or admin of an organisation.
statementstringThe statement as signed, canonical JSON. Its fields are repeated below for display.
saltstring32 bytes in hex: c of the entry is SHA-256(salt ‖ statement).
entrystringThe log entry with its signature, as the log holds it.
iintegerThe entry’s position in the log (the same as id).
publishedAtstring | nullThe earliest cosignature on the first checkpoint holding the entry.
checkpointobject | nullsize and note of the cosigned checkpoint that dates the entry. Null when the log could not be read.
inclusionarray<string> | nullThe entry’s RFC 6962 inclusion proof in that checkpoint, hex, deepest first.
platformstringThe platform of the account.
subjectstringThe account’s stable id, as GET /v1/lookup takes it.
displaystringThe handle the card shows for it.
statestringstolen, abandoned, moved or regained.
sincestring | nullFor stolen: the date it was stolen, YYYY-MM-DD.
toobject | nullFor moved: handle, and subject when the new account is linked to the card.
logIndexintegerThe entry’s position in the log.

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.
Cache-Controlno-storeAsk again each time: a new statement or alert shows at once.

Usage notes#

  • Signed by an active key of the person: a device key or the recovery key of the backup file, the key that is left when a device was stolen with the account.
  • At most 10 alerts per card in 24 hours. Organisations have none.

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.

400wrong_format

The request or the read belongs to the other log format than this server’s: a v1 record on a format 2 log, or a format 2 read on a format 1 log.

Read format in GET /v1/log (absent means 1) and send the shape of that format.

403identity_banned

The acting identity is suspended by Veil staff.

Wait for the restriction to end or ask staff to review it.

403key_banned

The device signing key is suspended by Veil staff.

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

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.

409action_pending

An earlier alert about the same account, or a staff action of the same family, is still waiting for the log.

Try again once it is published, within a few minutes.

409alert_in_force

The account already carries this same alert.

Nothing to do. To correct the date or the new account, send the alert with the new details.

409alert_not_in_force

regained ends an alert, and the account has none in force.

Read the card’s alerts (GET /v1/identities/{n}/alerts) before ending one.

409erase_pending

On a format 2 log, the card has an erasure waiting. Its signed closing names the current version, so the card cannot publish until the erasure is cancelled.

Cancel the erasure with POST /v1/identities/{n}/erase/cancel, then publish the version.

409signer_stale

On a format 2 log, signer.n of a signed entry is behind the current version of the card that signs.

Read the signing card again and sign the entry with its current version in signer.n.

410erased

The card existed and was erased or removed. The response also carries its public erasure receipt.

Stop using this card as current authority. Retain only the public receipt if needed.

422account_not_linked

The alert is about an account the card does not hold, or to.subject is not another account of the card on the same platform.

Name a linked account, or leave to.subject null and write the new handle.

422bad_entry

On a format 2 log, the salt, entry or closing of a request is wrong. error.message names the field.

Correct the named field, sign the entry again with the request key and send a new signed request.

422invalid_statement

A card’s alert or statement does not follow its schema: a missing or extra field, a text too long or with hidden characters, a link that is not https, a date in the future.

Fix the field named in error.message and sign the statement and its entry again.

422not_a_person

The card is an organisation where a person is required.

Use the organisation endpoint or an eligible person’s card.

429alert_quota

The card made 10 account alerts in the last 24 hours.

Wait the seconds that Retry-After gives.

503log_unavailable

The public log cannot currently be read, verified or used for publication.

For reads, keep the result unverified and try later. Before retrying a write, read the card to check whether publication completed.

Shared request errors
HTTPCodeMeaning and next step
400invalid_json

The body is not a JSON object, contains duplicate keys or has trailing data.

Send one valid JSON object with Content-Type application/json.

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.

409replayed_nonce

The signing key already used this nonce.

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

413payload_too_large

The request body exceeds this endpoint’s byte limit.

Reduce the encoded body. API-key management accepts at most 8 KiB.

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.