veil

Verify a message

Check a signature block or an explicit key, message and signature. This POST only reads.

POST/v1/verify
Access
Public; API key optional
Client
Browser fetch supported

JSON body#

NameLocationMeaning
blockJSONComplete pasted signature block. Maximum 32768 characters.
atJSONOptional Unix seconds. Defaults to now. Use a trusted publication timestamp.
key, message, signatureJSONRaw mode instead of block. Ed25519 uses hex, composite keys and signatures use base64. message is the exact UTF-8 text.
platform, subjectJSONOptional pair in raw mode to check account ownership at the chosen time.

Request examples#

javascript
const headers = new Headers();
headers.set("Content-Type", "application/json");
const response = await fetch("https://veil.rip/v1/verify", {
  method: "POST",
  headers,
  credentials: "omit",
  body: JSON.stringify({ block })
});
const verdict = await response.json();

Try the request#

Checking here sends the message to Veil. Use /verify for a check that stays in your browser.

Response#

HTTP 200application/json

valid is the overall verdict. signatureOk, identity, device, keyState, account and reason explain it. Block mode also returns the parsed block and the matching key when found.

FieldTypeMeaning
validbooleanCombined verdict for signature, key state and any requested account ownership. HTTP 200 also covers valid=false.
signatureOkbooleanWhether the cryptographic signature verifies.
identityIdentityRef | nullCard associated with the key or named in the block, when known.
devicestring | nullSigning device, when identified.
rolestring | nullSigning role, when identified.
keyStateKeyState | nullKey validity at the requested time.
accountobject | nullplatform, subject, owned and reason when platform/subject were requested.
reasonstring | nullNull when valid. Otherwise bad_signature, unknown_key, not_yet_valid, revoked, key_banned, identity_banned, not_linked, pending_takeover, identity_erased or unknown_identity. These are verdict reasons, not HTTP error codes.
keystring, optionalMatching public key for block mode; omitted if no key matched.
blockobject, optionalParsed block with n, url, message, alg, lang, quoted and flattened.

Example response

Illustrative values. Read the current response from your request.

json
{
  "valid": false,
  "signatureOk": true,
  "identity": null,
  "device": null,
  "role": null,
  "keyState": null,
  "account": null,
  "reason": "unknown_key"
}

Expand a nested object for its fields. Types ending in null allow a null value; optional fields may be absent. Other fields are present in that object.

Identity reference IdentityRef
FieldTypeMeaning
nintegerPermanent card number.
urlstringPermanent card URL.
namestringCurrent display name.
nameHiddenbooleanWhether staff hide the name.
Key state at a time KeyState
FieldTypeMeaning
atintegerTime checked, in Unix seconds.
activebooleanWhether the key was active at that time.
reasonstringactive, not_yet_valid or revoked.
validFromstringPublication time of the version that added it.
validUntilstring | nullPublication time of revocation, or null.

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.

What to verify#

This is the server’s verdict. To verify independently, check the card’s proof and verify the signature locally.

Usage notes#

  • block can be combined only with at. Send the key, message and signature form to include platform and subject. No request-signing headers are needed.
  • For a file or a release, the same path takes a signature with the file’s hashes or a checksum list: see Verify a file or a release.

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_block

The signature block cannot be parsed or names another directory.

Paste the complete block with its message and use the directory it names.

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_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.

413payload_too_large

The request body exceeds this endpoint’s byte limit.

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

When using an optional API key
HTTPCodeMeaning and next step
401invalid_api_key

The bearer credential is malformed, unknown, expired or revoked.

Use an active key. To use anonymous public access, remove Authorization entirely. Invalid credentials never fall back automatically.

403api_key_scope

The API key lacks read or verify scope, or the endpoint does not accept bearer keys.

Use the correct scope. Identity writes and key management still require a device signature.

403api_key_suspended

This API key is suspended.

Ask staff to restore it; another key cannot lift an account suspension.

403developer_suspended

The owner’s API account is suspended or the owner card is unavailable.

Check the owner account and any staff restriction. Creating another key does not bypass suspension.

Shared request errors
HTTPCodeMeaning and next step
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.