veil

Create an integration key

A person or organisation owner signs the request to issue a secret bearer key.

POST/v1/developer/accounts/{n}/keys
Access
Signed request
Client
Local or server client

Parameters#

NameLocationMeaning
nRequiredPathPermanent number of the person or organisation that owns the allowance.

JSON body#

NameLocationMeaning
labelRequiredJSONIntegration label, 1 through 60 characters.
scopesJSONArray containing read and/or verify. Both are enabled by default.
expiresInDaysJSON1 through 365, default 90.

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/developer/accounts/1/keys"

Response#

HTTP 201application/json

201 with key metadata and secret. The secret is returned only here and cannot be retrieved again.

FieldTypeMeaning
keyApiKeyCreated key metadata.
secretstringOne-time bearer credential. Save it now; no later response can retrieve it.

Example response

Illustrative values. Read the current response from your request.

json
{
  "key": {
    "id": "0123456789abcdef0123456789abcdef",
    "owner": 1,
    "label": "Website backend",
    "prefix": "veil_live_01234567…",
    "scopes": [
      "read",
      "verify"
    ],
    "state": "active",
    "createdAt": "2026-10-27T00:00:00Z",
    "expiresAt": "2027-01-25T00:00:00Z",
    "revokedAt": null,
    "suspended": false,
    "requestsPerMinute": null,
    "lastUsedAt": null,
    "requests": 0
  },
  "secret": "<save-the-one-time-secret-here>"
}

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.

API key metadata ApiKey
FieldTypeMeaning
idstringPublic key-management ID, not the bearer secret.
ownerintegerPermanent owner card number.
labelstringIntegration label supplied by its owner.
prefixstringNon-secret display prefix.
scopesarray<string>read and/or verify.
statestringactive, expired, suspended or revoked. Account suspension is reported separately.
createdAtstringCreation time in ISO 8601 UTC.
expiresAtstringExpiry time in ISO 8601 UTC.
revokedAtstring | nullPermanent revocation time.
suspendedbooleanWhether this key is suspended.
requestsPerMinuteinteger | nullAdditional per-key cap, or null.
lastUsedAtstring | nullLast admitted use, or null if never used.
requestsintegerLifetime admitted requests for this key.

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-storeDo not persist private or temporary responses in a shared cache.
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#

  • The owner signs with an active device key. Organisation management requires an accepted owner. Bearer API keys cannot authorise this call.
  • The response is private and uses Cache-Control no-store.
  • Management writes accept at most 8 KiB and reject unknown body fields. nonce and timestamp are the common signing fields.

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

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.

403identity_banned

The acting identity is suspended by Veil staff.

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

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.

409api_key_limit

The owner already has 20 unexpired, unrevoked API keys.

Revoke an unused key before creating another. Suspended keys still count.

413payload_too_large

The request body exceeds this endpoint’s byte limit.

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

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.