OpenAPI · REST reference
REST primitives for every WIDTH OpenAPI call — request envelope, signing, errors, idempotency, configuration, and file upload. Workflow endpoints (KYC, KYB, transactions, monitoring) are documented in their own pages.
Request envelope
Every OpenAPI call uses the same request envelope. The endpoint name, API version, key, and signature are passed as query parameters; the body is JSON.
POST {base_url}?api={apiName}&apiVersion=1.0.0&apiKey={apiKey}&sign={sign}
Content-Type: application/json
x-domain: {domainId}
{
...request body...
}
| Parameter | Where | Description |
|---|---|---|
api | query | API name, e.g. workflow.apply.submit |
apiVersion | query | API version (default 1.0.0) |
apiKey | query | Your public key (prefixed ak_) |
sign | query | HMAC-SHA256 hex string of the body, keyed by apiSecret |
x-domain | header | Business unit (domain) identifier |
Response format
All responses share the same envelope:
{
"apiCode": 200,
"apiMsg": "OK",
"data": { ... }
}
Errors use the same shape with a non-200 apiCode and a null data field:
{
"apiCode": 999001,
"apiMsg": "SIGN_ERROR",
"data": null
}
Error codes
| Code | Message | Description | Action |
|---|---|---|---|
200 | OK | Success | — |
999000 | GATEWAY_ERROR | General gateway error | Contact support |
999001 | SIGN_ERROR | Signature verification failed | See authentication |
999301 | API_INVALID_REQUEST | Malformed request | Check request body |
999302 | API_NOT_FOUND | API not found | Verify api parameter |
999303 | API_NON_AUTHORIZED | Unauthorized | Check tenant permissions |
999401 | SERVICE_TIME_OUT | Backend timeout | Retry with backoff |
999402 | SERVICE_INVOKE_ERROR | Invocation failed | Retry or contact support |
Authentication
Every request is signed with HMAC-SHA256 over the raw request body. The signature is sent as the sign query parameter.
sign = HmacSHA256(apiSecret, requestBody)
Both the key and body use UTF-8 encoding. The result is a 64-character lowercase hexadecimal string.
import hmac, hashlib
sign = hmac.new(
api_secret.encode('utf-8'),
request_body.encode('utf-8'),
hashlib.sha256
).hexdigest()
const crypto = require('crypto');
const sign = crypto
.createHmac('sha256', apiSecret)
.update(requestBody, 'utf8')
.digest('hex');
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiSecret.getBytes("UTF-8"), "HmacSHA256"));
byte[] hash = mac.doFinal(requestBody.getBytes("UTF-8"));
String sign = HexFormat.of().formatHex(hash);
apiSecret in client code. Signing must happen on a server you control. The SDKs use a session-scoped sdkToken instead.Idempotency
All node-data submission APIs are idempotent by callbackKey. Submitting the same callbackKey multiple times is safe — the workflow advances only once.
Use idempotency for safe retries on network failure or timeouts. Persist your callbackKey with the original request and reuse it on retry.
Interaction model
Client-side: chain-driven flow
Your frontend is driven by the nextStep object returned in each submission response. Each step tells your UI which node is next and what to collect.
Server-side: status polling
For server-to-server flows, poll workflow.execution.status at relaxed intervals (1–5 minutes) until a terminal state. See execution statuses.
Configuration
Endpoint: prepare.avira.config (POST). Returns reference data used by workflow nodes — country lists, document types, industry codes.
| Key | Description | Used by |
|---|---|---|
particular | Personal / corporate field definitions | Particular nodes |
required-form | Form templates with validation rules | Required Forms nodes |
avira_countries | Country list (ISO codes + names) | Nationality, residence |
avira_document_master | Supported documents per country | OCR & upload |
avira_ssic | Industry classification codes | Industry field |
avira_ssoc | Occupation classification codes | Occupation field |
avira_phone_code | Country phone codes | Phone verification |
avira_entity_types | Corporate entity types | KYB particular |
File upload
Two-step upload: request a short-lived token, then POST the file.
Step 1 — get upload token
Endpoint: storage.upload.token (POST). Body: {}.
{
"apiCode": 200,
"data": {
"uploadToken": "a1b2c3d4...",
"expiresIn": 900
}
}
Tokens are valid for 15 minutes.
Step 2 — upload the file
Multipart:
curl -X POST "${STORAGE_URL}/api/v1/upload/file" \
-F "uploadToken=a1b2c3d4..." \
-F "file=@passport_front.jpg"
Base64 (for in-memory images):
curl -X POST "${STORAGE_URL}/api/v1/upload" \
-H "Content-Type: application/json" \
-d '{
"uploadToken": "a1b2c3d4...",
"image": "data:image/jpeg;base64,/9j/4AAQ...",
"filename": "passport_front.jpg"
}'
Supported types: PNG, JPG, JPEG, GIF, WEBP, TIFF, PDF. Max size: 20 MB.
Error handling & retry strategy
| Scenario | Recommendation |
|---|---|
| Signature error (403) | Do not retry; fix signing implementation |
| Server error (500) | Retry 3× with exponential backoff (1s → 2s → 4s) |
| Status polling | Every 2–5 seconds, max 30 minutes |
| Upload token 401 | Get a new token, retry once |
| OTP verify fail | Resend OTP if max attempts exhausted |
| Network timeout | Safe to retry — all submission APIs are idempotent |