Developers · API reference

Reference for theAPI Full

A precise technical reference for every endpoint and object in the Wthaiq API: authentication, errors, pagination, Idempotency, rate limits, signature requests, signers, documents, templates, identity verification, verification, events and Webhooks. Ready examples in cURL and Node for every call.

BASE URL https://wthaiq.com/api/v1
Version 2026-07-01 Authentication Bearer sk_... Response application/json
create-signature-request.sh
curl -X POST https://wthaiq.com/api/v1/signature_requests \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f3c9c2e-..." \
  -d '{
    "title": "Employment contract — Ahmed M.",
    "source": { "type": "template", "template_id": "tpl_employment" },
    "legal_level": "aes",
    "ordered": true,
    "signers": [
      { "name": "Ahmed Mohamed", "email": "ahmed@example.com",
        "method": "draw", "require_identity": true }
    ]
  }'

Overview

The Wthaiq API is a REST API organised around resources; it uses predictable HTTP paths, encodes errors with standard HTTP codes, and sends and receives payloads of application/json (except for file uploads, which use multipart/form-data, and downloading the PDF, which is application/pdf). All requests go over HTTPS only.

Timestamps are integers in Unix epoch seconds. Every resource carries a unique ID prefixed with its type, which makes tracing objects in logs straightforward:

PrefixResourceExample
sr_signature_requestsr_3n8Kd2Qa1V
sgr_signersgr_9fA2
doc_documentdoc_7Yq
tpl_templatetpl_employment
idv_identity_verificationidv_5k
evt_eventevt_2M
we_webhook_endpointwe_1a
key_api_keykey_88
Base URL: All paths are relative to https://wthaiq.com/api/v1. The version is pinned by date through the header Wthaiq-Version, and the changes are dated in Changelog.

Authentication

Requests are authenticated with a secret key in the header Authorization using the HTTP Bearer scheme. Your keys carry full privileges; keep them on the server only, and never place them in client-side code, public repositories or mobile applications.

KeyThe formatDescription
The secret keysk_...Full privileges, server-side only. Live from creation: it sends real messages, issues legal certificates, and is billed. See No test mode.
The publishable keypk_...Safe to expose in the browser, locked to your domains, and limited to a fixed set of paths.
The Webhook secretwhsec_...Used to verify the signature of Webhook messages (HMAC-SHA256).
auth.sh
curl https://wthaiq.com/api/v1/signature_requests \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Security warning: Any request without a valid key is rejected with status 401 and an error type authentication_error. Manage your keys and delete any leaked key from the dashboard immediately.

Versioning

The version is pinned by date. Send the header Wthaiq-Version with the date of the version you built your integration against, so API behaviour stays fixed even if we ship later changes. If you omit the header, the latest version pinned to your account is used.

version.sh
curl https://wthaiq.com/api/v1/templates \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Every breaking change ships under a new date and is documented in Changelog. Non-breaking additions (new fields, new enum values) may appear without a change of date, so design your client to tolerate unexpected fields.

Errors

Wthaiq uses conventional HTTP response codes: the range 2xx success, and the range 4xx indicates an error in the input, and the range 5xx indicates an error on our servers. Every error is returned inside an envelope error consistent, carrying a type, a code and a message, and usually the name of the offending parameter and the request ID.

error.json
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_missing",
    "message": "The signers parameter is required.",
    "param": "signers",
    "request_id": "req_9f2"
  }
}
Error types and HTTP codes
TypeHTTP codeMeaning
authentication_error401Missing or invalid key.
invalid_request_error400 / 404A missing or invalid parameter, or a resource that does not exist.
quota_error402Quota exceeded, or a higher plan is required.
rate_limit_error429More requests than allowed.
identity_error422Identity verification failed or could not be completed.
signing_error422Signing could not be completed (invalid state for the operation).
idempotency_error409Idempotency key conflict.
api_error5xxAn internal error on the Wthaiq servers.
Other HTTP codes: 200 Success · 201 Created · 403 Forbidden · domain 5xx Server error.

Idempotency

to make retries safe on requests POST, send the header Idempotency-Key with a unique UUID for each logical operation. If the same request arrives twice (because of a dropped network connection, for example), we return the original response instead of creating a duplicate resource. Keys are retained for 24 hours.

idempotency.sh
curl -X POST https://wthaiq.com/api/v1/signature_requests \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f3c9c2e-8b1a-4c7d-9e21-2f0a6b3d1c44" \
  -d '{ "title": "Employment contract — Ahmed M." }'
If you reuse the same key with a different request body, the status returned is 409 and an error type idempotency_error.

Rate limits

The default limit in live mode is 100 requests per 10 seconds. Every response carries headers showing your remaining quota, and when you exceed it the status returned is 429 with the header Retry-After with the number of seconds to wait before retrying.

The headerDescription
X-RateLimit-LimitThe maximum number of requests in the current window.
X-RateLimit-RemainingThe number of requests remaining in the current window.
X-RateLimit-ResetThe Unix timestamp at which the window resets.
Retry-AfterAppears with status 429: the number of seconds to wait before retrying.
429.txt
# HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1754000010
Retry-After: 4
Best practice: implement retries with exponential backoff when you receive a 429, and respect the value of Retry-After.

Request IDs

Every response carries the header Wthaiq-Request-Id with a value prefixed byreq_, and the same ID appears inside the field request_id on any error. Store this ID in your logs; it is what we ask for when you contact support, so we can trace any request precisely.

request-id.txt
# HTTP/1.1 200 OK
Content-Type: application/json
Wthaiq-Request-Id: req_9f2a1c

No test mode — sk_ versus pk_

There is no separate test environment in Wthaiq. Every sk_ Live from the moment it is created: it sends real email to signers, issues legally effective certificates, and charges your balance with every call. The field livemode equals true on every object, always — so do not check it to tell one environment from another.

The real distinction between keys is not test/live, but Access scope:

Aspectsk_ (secret)pk_ (publishable)
Where it is usedServer only — never exposedSafe to expose in the browser
Permissionsfull across all resourcesLimited to specific paths (templates, signature requests, signers, flows) for reading, with the creation of signature requests only
Domain lockingThere is noLocked to the domains allowed_origins that you define
BillingEvery call is genuinely billedEvery call is genuinely billed
Every call is real and billed. There is no separate test balance and no free calls. Live pricing only: EGP 20 per signature request, plus EGP 35 per signer who requires identity verification.
The safe way to test: Top up a small balance and use your own email address as the recipient of the first signature request you create while building your integration, instead of real customer addresses. Use sk_ on your server only, andpk_ locked to your domain for any code running in the browser.

Metadata

Most creatable objects accept the field metadata: a string key/value map in which you store your own internal references (an order number, a user ID in your system, a campaign tag). We do not use these values internally, and they are returned unchanged in every response and in Webhook messages.

ConstraintLimit
Number of keysUp to 50 keys per object.
Key lengthUp to 40 characters.
Value lengthUp to 500 characters.
metadata.json
{
  "metadata": {
    "order_id": "A-1024",
    "crm_contact": "cust_58231",
    "campaign": "q3-onboarding"
  }
}

Signature Requests

The signature request is the central object: it represents a document (from a template or an uploaded file) sent to one or more signers at a defined legal (assurance) level, with full tracking of each signer's status through to completion, at which point the request is sealed with an authenticated, independently verifiable evidence record.

/v1/signature_requests
POST/v1/signature_requests

Create a new signature request — as a draft, or sent to the signers immediately.

The fieldTypeRequiredDescription
titlestringRequiredThe request title shown to the signers.
sourceobjectRequiredThe source of the document. type either template with template_id, or document with document_id.
legal_levelstringRequiredLegal level: ses or aes or qes.
signersarrayRequiredThe list of signers (see the fields below).
formatstringOptionalThe requested assurance level; it is recorded and returned on the object: pades-b / pades-t / pades-lt / pades-lta (default pades-lt). The API path does not currently issue a PAdES signature embedded inside the PDF file; the cryptographic evidence is the sealed evidence record (an Ed25519 signature that any party can verify with the public key at /trust, plus an RFC 3161 timestamp).
orderedbooleanOptionalSequential signing according to the field order (default false).
require_identitybooleanOptionalRequire identity verification before signing, at the request level.
ccarrayOptionalCopy recipients — they receive the signed contract on completion without signing (up to 20). Each item { name, email }.
remindersobjectOptionalReminder settings: enabled, interval_hours, max.
expires_atintegerOptionalThe Unix timestamp of the request's expiry.
sendbooleanOptionalSend immediately after creation; otherwise it is created as a draft.
metadataobjectOptionalKey/value metadata.
signers[].namestringRequiredThe signer's name.
signers[].emailstringRequiredThe signer's email.
signers[].methodstringOptionaldraw (a drawn signature + OTP) or token (a QES certificate on a hardware token).
signers[].require_identitybooleanOptionalIdentity verification for this specific signer (AES).
signers[].orderintegerOptionalThe signing order when ordered: true.
signers[].fieldsobjectOptionalThe pre-filled template field values.
cURLNode
request.sh
curl -X POST https://wthaiq.com/api/v1/signature_requests \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f3c9c2e-..." \
  -d '{
    "title": "Employment contract — Ahmed M.",
    "source": { "type": "template", "template_id": "tpl_employment" },
    "legal_level": "aes",
    "ordered": true,
    "signers": [
      { "name": "Ahmed Mohamed", "email": "ahmed@example.com", "type": "individual",
        "method": "draw", "require_identity": true,
        "fields": { "job_title": "Software Engineer", "salary": "25000" } }
    ],
    "reminders": { "enabled": true, "interval_hours": 48, "max": 3 },
    "metadata": { "order_id": "A-1024" }
  }'
create.js
import Wthaiq from '@wthaiq/node';
const wt = new Wthaiq('sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');

const sr = await wt.signatureRequests.create({
  title: 'Employment contract — Ahmed M.',
  source: { type: 'template', template_id: 'tpl_employment' },
  legal_level: 'aes',
  ordered: true,
  signers: [
    { name: 'Ahmed Mohamed', email: 'ahmed@example.com', method: 'draw', require_identity: true }
  ]
});
Response · 201 Created
signature_request.json
{
  "id": "sr_3n8Kd2Qa1V",
  "object": "signature_request",
  "livemode": true,
  "status": "sent",
  "title": "Employment contract — Ahmed M.",
  "legal_level": "aes",
  "format": "pades-lt",
  "source": { "type": "template", "template_id": "tpl_employment" },
  "signers": [
    {
      "id": "sgr_9fA2",
      "object": "signer",
      "name": "Ahmed Mohamed",
      "email": "ahmed@example.com",
      "type": "individual",
      "method": "draw",
      "require_identity": true,
      "order": 1,
      "status": "sent",
      "signing_url": "https://sign.wthaiq.com/s/uZ8..",
      "viewed_at": null,
      "signed_at": null,
      "identity": { "status": "pending", "provider": "didit", "level": "aes" },
      "fields": { "job_title": "Software Engineer", "start_date": "2026-08-01" }
    }
  ],
  "ordered": true,
  "require_identity": true,
  "reference": null,
  "reminders": { "enabled": true, "interval_hours": 48, "max": 3 },
  "expires_at": 1755000000,
  "completed_at": null,
  "download_url": null,
  "metadata": { "order_id": "A-1024" },
  "created_at": 1754000000
}
POST/v1/signature_requests/bulk

Bulk send: the same contract to several signers at once — creates A separate signature request for each signer (up to 200). The balance is checked for the entire batch before it starts.

ParameterTypeRequiredDescription
sourceobjectRequiredThe contract source — such as { "type": "template", "template_id": "tpl_..." }.
recipientsarrayRequiredThe list of signers; each item { name, email }.
fieldsobjectOptionalThe shared field values for all the contracts.
legal_levelstringOptionalses or aes or qes.
ccarrayOptionalCopy recipients for each contract.
terminal
curl -X POST https://wthaiq.com/api/v1/signature_requests/bulk \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "type": "template", "template_id": "tpl_265" },
    "recipients": [
      { "name": "Mohamed Salem", "email": "m1@example.com" },
      { "name": "Sara Ahmed", "email": "s2@example.com" }
    ],
    "fields": { "amount": "5000" },
    "legal_level": "ses"
  }'
Response
{
  "object": "bulk_send",
  "created": 2,
  "failed": 0,
  "requests": [
    { "signature_request": "sr_3n8Kd2Qa1V", "email": "m1@example.com", "status": "sent" },
    { "signature_request": "sr_9Kx2Ld7Pq3", "email": "s2@example.com", "status": "sent" }
  ],
  "errors": []
}
GET/v1/signature_requests

List signature requests with cursor pagination, with optional filtering by status and date (created_after / created_before).

ParameterTypeRequiredDescription
limitintegerOptionalFrom 1 to 100 (default 20).
starting_afterstringOptionalThe cursor for the next page.
ending_beforestringOptionalThe previous page cursor.
statusstringOptionalFilter by status: draft, sent, completed ...
cURLNode
list.sh
curl "https://wthaiq.com/api/v1/signature_requests?limit=3&status=sent" \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
list.js
const list = await wt.signatureRequests.list({ limit: 3, status: 'sent' });
Response · 200 OK
list.json
{
  "object": "list",
  "data": [
    {
      "id": "sr_3n8Kd2Qa1V",
      "object": "signature_request",
      "livemode": true,
      "status": "sent",
      "title": "Employment contract — Ahmed M.",
      "legal_level": "aes",
      "format": "pades-lt",
      "created_at": 1754000000
    }
  ],
  "has_more": true,
  "next_cursor": "sr_2m7Xa9"
}
GET/v1/signature_requests/{id}

Retrieve a signature request by its ID, with its list of signers and their status.

ParameterTypeRequiredDescription
idstring · pathRequiredThe signature request ID (sr_).
cURLNode
retrieve.sh
curl https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
retrieve.js
const sr = await wt.signatureRequests.retrieve('sr_3n8Kd2Qa1V');
Response · 200 OK
signature_request.json
{
  "id": "sr_3n8Kd2Qa1V",
  "object": "signature_request",
  "livemode": true,
  "status": "partially_signed",
  "title": "Employment contract — Ahmed M.",
  "legal_level": "aes",
  "format": "pades-lt",
  "source": { "type": "template", "template_id": "tpl_employment" },
  "ordered": true,
  "require_identity": true,
  "reference": null,
  "expires_at": 1755000000,
  "completed_at": null,
  "download_url": null,
  "metadata": { "order_id": "A-1024" },
  "created_at": 1754000000
}
POST/v1/signature_requests/{id}/send

Send a request from draft status and push it to the signers.

ParameterTypeRequiredDescription
idstring · pathRequiredThe signature request ID.
send.sh
curl -X POST https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/send \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
signature_request.json
{
  "id": "sr_3n8Kd2Qa1V",
  "object": "signature_request",
  "status": "sent",
  "created_at": 1754000000
}
POST/v1/signature_requests/{id}/cancel

Cancel a signature request that has not yet completed; its status becomes canceled.

ParameterTypeRequiredDescription
idstring · pathRequiredThe signature request ID.
cancel.sh
curl -X POST https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/cancel \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
signature_request.json
{
  "id": "sr_3n8Kd2Qa1V",
  "object": "signature_request",
  "status": "canceled",
  "completed_at": null,
  "created_at": 1754000000
}
POST/v1/signature_requests/{id}/reminders

Trigger an immediate reminder to the pending signers on this request.

ParameterTypeRequiredDescription
idstring · pathRequiredThe signature request ID.
reminders.sh
curl -X POST https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/reminders \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
reminder.json
{
  "object": "reminder",
  "signature_request": "sr_3n8Kd2Qa1V",
  "sent_to": ["ahmed@example.com"],
  "sent_at": 1754050000
}
GET/v1/signature_requests/{id}/download

Downloading the signed PDF (application/pdf) — available only once the request is complete.

ParameterTypeRequiredDescription
idstring · pathRequiredThe ID of the completed signature request.
download.sh
curl https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/download \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -o agreement-signed.pdf
Response · 200 OK (headers)
headers.txt
# HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="agreement-signed.pdf"
Wthaiq-Request-Id: req_9f2a1c
If the request is not complete, the status returned is 422 and an error type signing_error.
GET/v1/signature_requests/{id}/certificate

The completion certificate with the audit trail — PDF by default, or JSON through ?format=json.

ParameterTypeRequiredDescription
idstring · pathRequiredThe signature request ID.
formatstring · queryOptionalpdf (default) or json.
certificate.sh
curl "https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/certificate?format=json" \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
certificate.json
{
  "object": "certificate",
  "signature_request": "sr_3n8Kd2Qa1V",
  "reference": "WTQ-000123",
  "legal_level": "aes",
  "format": "pades-lt",
  "document": { "sha256": "a3f1..", "sealed_at": 1754500000 },
  "signers": [
    { "name": "Ahmed Mohamed", "signed_at": 1754500000, "identity": "approved" }
  ],
  "events_count": 7,
  "issued_at": 1754500050
}
GET/v1/signature_requests/{id}/events

The chronological audit trail for this request — every event from opening to signing.

ParameterTypeRequiredDescription
idstring · pathRequiredThe signature request ID.
limitinteger · queryOptionalFrom 1 to 100 (default 20).
events.sh
curl https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/events \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
events.json
{
  "object": "list",
  "data": [
    {
      "id": "evt_2M",
      "object": "event",
      "type": "signer.signed",
      "signature_request": "sr_3n8Kd2Qa1V",
      "signer": "sgr_9fA2",
      "actor": "signer",
      "ip": "197.44.x.x",
      "user_agent": "..",
      "created_at": 1754500000
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Signers

A signer represents a party to a signature request, with its own status, signing method and identity verification result. It is created implicitly when the request is created, and it can be retrieved or given an embedded signing session.

/v1/signers
GET/v1/signers/{id}

Retrieve a signer by ID.

ParameterTypeRequiredDescription
idstring · pathRequiredThe signer ID (sgr_).
retrieve.sh
curl https://wthaiq.com/api/v1/signers/sgr_9fA2 \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
signer.json
{
  "id": "sgr_9fA2",
  "object": "signer",
  "name": "Ahmed Mohamed",
  "email": "ahmed@example.com",
  "type": "individual",
  "method": "draw",
  "require_identity": true,
  "order": 1,
  "status": "viewed",
  "signing_url": "https://sign.wthaiq.com/s/uZ8..",
  "viewed_at": 1754000100,
  "signed_at": null,
  "identity": { "status": "approved", "provider": "didit", "level": "aes" },
  "fields": { "job_title": "Software Engineer", "start_date": "2026-08-01" }
}
POST/v1/signers/{id}/signing_session

Generate a short-lived embedded signing link/token (white-label) to display the signing page inside your app.

The fieldTypeRequiredDescription
idstring · pathRequiredThe signer ID.
expires_inintegerOptionalThe session lifetime in seconds (default 3600).
redirect_urlstringOptionalA URL the signer is returned to after completion.
signing_session.sh
curl -X POST https://wthaiq.com/api/v1/signers/sgr_9fA2/signing_session \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -d '{ "expires_in": 3600, "redirect_url": "https://app.acme.com/done" }'
Response · 201 Created
signing_session.json
{
  "object": "signing_session",
  "signer": "sgr_9fA2",
  "signing_url": "https://sign.wthaiq.com/s/uZ8..?t=st_9Qa..",
  "token": "st_9Qa1V..",
  "expires_at": 1754003600
}

Documents

A document represents an uploaded PDF file on which signature requests are built (as an alternative to the ready-made templates). It is stored with its SHA-256 hash, its page count and its size.

/v1/documents
POST/v1/documents

Upload a PDF file via multipart/form-data to be used as a document source.

The fieldTypeRequiredDescription
filefileRequiredThe PDF file (multipart form field).
kindstringOptionalDocument type: pdf (default) or contract.
field_mapstring (JSON)OptionalA coordinate field map that prints the values in their places on the pages. The coordinates relative (0..1) from the top left of the page.
Field map (field_map): An array of items in the form { "key", "label", "type", "page", "x", "y", "w", "h", "required" }. When a signature request is sent with this document, the values of fields at exactly the specified positions.
upload.sh
curl -X POST https://wthaiq.com/api/v1/documents \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -F "file=@agreement.pdf" \
  -F "kind=pdf" \
  -F 'field_map=[
    {"key":"signer_name","label":"Signer name","type":"text",
     "page":1,"x":0.12,"y":0.34,"w":0.30,"h":0.04,"required":true},
    {"key":"contract_date","label":"Date","type":"date",
     "page":1,"x":0.62,"y":0.34,"w":0.22,"h":0.04}
  ]'
Response · 201 Created
document.json
{
  "id": "doc_7Yq",
  "object": "document",
  "kind": "pdf",
  "filename": "agreement.pdf",
  "pages": 4,
  "bytes": 183221,
  "sha256": "a3f1..",
  "created_at": 1754000000
}

Templates

Templates are the ready-made contracts (more than 250 templates) with their fillable fields. Signature requests reference them via source.template_id.

/v1/templates
GET/v1/templates

List the ready-made templates, with optional filtering by category.

ParameterTypeRequiredDescription
limitinteger · queryOptionalFrom 1 to 100 (default 20).
categorystring · queryOptionalFilter by category, such as hr.
starting_afterstring · queryOptionalThe cursor for the next page.
cURLNode
list.sh
curl "https://wthaiq.com/api/v1/templates?category=hr" \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
list.js
const tpls = await wt.templates.list({ category: 'hr' });
Response · 200 OK
list.json
{
  "object": "list",
  "data": [
    {
      "id": "tpl_employment",
      "object": "template",
      "name": "Employment contract",
      "category": "hr",
      "languages": ["ar"],
      "created_at": 1754000000
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/templates/{id}

Retrieve a template together with the definition of its fillable fields.

ParameterTypeRequiredDescription
idstring · pathRequiredThe template ID (tpl_).
cURLNode
retrieve.sh
curl https://wthaiq.com/api/v1/templates/tpl_employment \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
retrieve.js
const tpl = await wt.templates.retrieve('tpl_employment');
Response · 200 OK
template.json
{
  "id": "tpl_employment",
  "object": "template",
  "name": "Employment contract",
  "category": "hr",
  "languages": ["ar"],
  "fields": [
    { "key": "job_title", "label": "Job title", "type": "text", "required": true },
    { "key": "salary", "label": "Salary", "type": "number", "required": true }
  ],
  "created_at": 1754000000
}

Identity Verifications

Identity verification ties the signer's signature to a real person verified through Didit (an official document and a live face match), and it is a requirement of the advanced level (AES).

/v1/identity_verifications
GET/v1/identity_verifications/{id}

Retrieve an identity verification by its ID.

ParameterTypeRequiredDescription
idstring · pathRequiredThe identity verification ID (idv_).
retrieve.sh
curl https://wthaiq.com/api/v1/identity_verifications/idv_5k \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
identity_verification.json
{
  "id": "idv_5k",
  "object": "identity_verification",
  "provider": "didit",
  "signer": "sgr_9fA2",
  "status": "approved",
  "level": "aes",
  "verification_url": "https://verify.wthaiq.com/i/..",
  "created_at": 1754000000
}

Verifications

Public verification of a signed document's integrity through its public reference (WTQ- / WTH-), without exposing the parties' full details. It compares the document's current hash with the sealed one.

/v1/verifications
GET/v1/verifications/{reference}

Public verification of a document through its reference.

ParameterTypeRequiredDescription
referencestring · pathRequiredThe public reference, such as WTQ-000123.
verify.sh
curl https://wthaiq.com/api/v1/verifications/WTQ-000123 \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
verification.json
{
  "object": "verification",
  "reference": "WTQ-000123",
  "found": true,
  "status": "completed",
  "integrity": "intact",
  "document": {
    "title": "Employment contract",
    "sha256": "a3f1..",
    "format": "pades-lt",
    "legal_level": "aes"
  },
  "signed_at": 1754500000,
  "parties": [
    { "name": "A**** M****", "role": "signer" },
    { "name": "C**** W****", "role": "owner" }
  ],
  "qr": "https://wthaiq.com/verify?c=WTQ-000123"
}

Events

Every event is an immutable object that represents an entry in the audit trail and reflects a change in a resource. These same events are what Webhook messages deliver, and they can be listed and retrieved later.

/v1/events
GET/v1/events

List the account events, with optional filtering by type.

ParameterTypeRequiredDescription
limitinteger · queryOptionalFrom 1 to 100 (default 20).
typestring · queryOptionalFilter by event type, such as signer.signed.
starting_afterstring · queryOptionalThe cursor for the next page.
list.sh
curl "https://wthaiq.com/api/v1/events?limit=10&type=signer.signed" \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
list.json
{
  "object": "list",
  "data": [
    {
      "id": "evt_2M",
      "object": "event",
      "type": "signer.signed",
      "signature_request": "sr_3n8Kd2Qa1V",
      "signer": "sgr_9fA2",
      "actor": "signer",
      "ip": "197.44.x.x",
      "user_agent": "..",
      "created_at": 1754500000
    }
  ],
  "has_more": true,
  "next_cursor": "evt_1L"
}
GET/v1/events/{id}

Retrieve a single event by its ID.

ParameterTypeRequiredDescription
idstring · pathRequiredThe event ID (evt_).
retrieve.sh
curl https://wthaiq.com/api/v1/events/evt_2M \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
event.json
{
  "id": "evt_2M",
  "object": "event",
  "type": "signer.signed",
  "signature_request": "sr_3n8Kd2Qa1V",
  "signer": "sgr_9fA2",
  "actor": "signer",
  "ip": "197.44.x.x",
  "user_agent": "..",
  "created_at": 1754500000
}

Webhook Endpoints

A Webhook endpoint registers a URL that receives real-time notifications of the events you subscribe to. See the guide Webhooks to verify the signature and to retry.

/v1/webhook_endpoints
POST/v1/webhook_endpoints

Create a new Webhook endpoint. The secret value is returned whsec_ once, at creation.

The fieldTypeRequiredDescription
urlstringRequiredThe HTTPS URL that receives POST messages.
enabled_eventsarrayRequiredThe event types subscribed to, such as signature_request.completed.
cURLNode
create.sh
curl -X POST https://wthaiq.com/api/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.acme.com/hooks/wthaiq",
    "enabled_events": ["signature_request.completed", "signer.signed"]
  }'
create.js
const we = await wt.webhookEndpoints.create({
  url: 'https://api.acme.com/hooks/wthaiq',
  enabled_events: ['signature_request.completed', 'signer.signed']
});
Response · 201 Created
webhook_endpoint.json
{
  "id": "we_1a",
  "object": "webhook_endpoint",
  "url": "https://api.acme.com/hooks/wthaiq",
  "enabled_events": ["signature_request.completed", "signer.signed"],
  "status": "enabled",
  "secret": "whsec_..",
  "created_at": 1754000000
}
GET/v1/webhook_endpoints

List the registered Webhook endpoints.

ParameterTypeRequiredDescription
limitinteger · queryOptionalFrom 1 to 100 (default 20).
list.sh
curl https://wthaiq.com/api/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
list.json
{
  "object": "list",
  "data": [
    {
      "id": "we_1a",
      "object": "webhook_endpoint",
      "url": "https://api.acme.com/hooks/wthaiq",
      "enabled_events": ["signature_request.completed", "signer.signed"],
      "status": "enabled",
      "created_at": 1754000000
    }
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/webhook_endpoints/{id}

Retrieve a Webhook endpoint by its ID.

ParameterTypeRequiredDescription
idstring · pathRequiredThe Webhook endpoint ID (we_).
retrieve.sh
curl https://wthaiq.com/api/v1/webhook_endpoints/we_1a \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
webhook_endpoint.json
{
  "id": "we_1a",
  "object": "webhook_endpoint",
  "url": "https://api.acme.com/hooks/wthaiq",
  "enabled_events": ["signature_request.completed", "signer.signed"],
  "status": "enabled",
  "secret": "whsec_..",
  "created_at": 1754000000
}
DELETE/v1/webhook_endpoints/{id}

Delete a Webhook endpoint; events stop being sent to it immediately.

ParameterTypeRequiredDescription
idstring · pathRequiredThe Webhook endpoint ID.
delete.sh
curl -X DELETE https://wthaiq.com/api/v1/webhook_endpoints/we_1a \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01"
Response · 200 OK
deleted.json
{
  "id": "we_1a",
  "object": "webhook_endpoint",
  "deleted": true
}
signature_request

The central object of a signature request.

signature_request
{
  "id": "sr_3n8Kd2Qa1V",
  "object": "signature_request",
  "livemode": true,
  "status": "sent",
  "title": "Employment contract — Ahmed M.",
  "legal_level": "aes",
  "format": "pades-lt",
  "source": { "type": "template", "template_id": "tpl_employment" },
  "signers": [ ],
  "ordered": true,
  "require_identity": true,
  "reference": null,
  "reminders": { "enabled": true, "interval_hours": 48, "max": 3 },
  "expires_at": 1755000000,
  "completed_at": null,
  "download_url": null,
  "metadata": { "order_id": "A-1024" },
  "created_at": 1754000000
}
The fieldTypeDescription
idstringThe request ID.
statusstringdraft · sent · partially_signed · completed · declined · expired · canceled.
legal_levelstringses · aes · qes.
formatstringThe requested assurance level recorded on the object (pades-b / t / lt / lta). The API path does not issue a PAdES signature embedded inside the PDF; the proof is the sealed evidence record, which is independently verifiable.
sourceobjectThe source of the document (a template or an uploaded document).
signersarrayThe signer objects.
orderedbooleanSequential signing.
require_identitybooleanIdentity verification before signing (AES).
referencestring · nullThe public verification code (WTQ-XXXXXX) on completion.
download_urlstring · nullThe signed PDF URL, which appears on completion.
created_atintegerThe Unix timestamp of creation.
signer

A signing party within a request.

signer
{
  "id": "sgr_9fA2",
  "object": "signer",
  "name": "Ahmed Mohamed",
  "email": "ahmed@example.com",
  "type": "individual",
  "method": "draw",
  "require_identity": true,
  "order": 1,
  "status": "viewed",
  "signing_url": "https://sign.wthaiq.com/s/uZ8..",
  "viewed_at": 1754000100,
  "signed_at": null,
  "identity": { "status": "approved", "provider": "didit", "level": "aes" },
  "fields": { "job_title": "Software Engineer", "start_date": "2026-08-01" }
}
The fieldTypeDescription
typestringindividual or company.
methodstringdraw (drawn signature + email OTP) or token (a QES certificate on a hardware token).
statusstringpending · sent · viewed · otp_verified · identity_verified · signed · declined.
signing_urlstringThe hosted or embeddable signing page.
identityobjectThe identity verification result at require_identity.
fieldsobjectThe pre-filled template field values.
document

An uploaded PDF file.

document
{
  "id": "doc_7Yq",
  "object": "document",
  "kind": "pdf",
  "filename": "agreement.pdf",
  "pages": 4,
  "bytes": 183221,
  "sha256": "a3f1..",
  "created_at": 1754000000
}
The fieldTypeDescription
kindstringpdf or contract.
pagesintegerThe page count.
bytesintegerThe file size in bytes.
sha256stringThe document's SHA-256 hash.
template

A ready-made contract with fillable fields.

template
{
  "id": "tpl_employment",
  "object": "template",
  "name": "Employment contract",
  "category": "hr",
  "languages": ["ar"],
  "fields": [
    { "key": "job_title", "label": "Job title", "type": "text", "required": true },
    { "key": "salary", "label": "Salary", "type": "number", "required": true }
  ],
  "created_at": 1754000000
}
The fieldTypeDescription
namestringThe template name.
categorystringThe template category, such as hr.
languagesarrayThe languages available for the template.
fieldsarrayField definitions: key, label, type, required.
identity_verification

The result of a signer's identity verification through Didit.

identity_verification
{
  "id": "idv_5k",
  "object": "identity_verification",
  "provider": "didit",
  "signer": "sgr_9fA2",
  "status": "approved",
  "level": "aes",
  "verification_url": "https://verify.wthaiq.com/i/..",
  "created_at": 1754000000
}
The fieldTypeDescription
providerstringThe verification provider (didit).
signerstringThe associated signer ID.
statusstringpending · approved · declined · in_review.
levelstringThe target legal level.
verification

The result of a public check of a document's integrity by its reference.

verification
{
  "object": "verification",
  "reference": "WTQ-000123",
  "found": true,
  "status": "completed",
  "integrity": "intact",
  "document": {
    "title": "Employment contract",
    "sha256": "a3f1..",
    "format": "pades-lt",
    "legal_level": "aes"
  },
  "signed_at": 1754500000,
  "parties": [
    { "name": "A**** M****", "role": "signer" },
    { "name": "C**** W****", "role": "owner" }
  ],
  "qr": "https://wthaiq.com/verify?c=WTQ-000123"
}
The fieldTypeDescription
referencestringThe public reference (WTQ-/WTH-).
foundbooleanWhether the document was found.
integritystringintact · modified · unknown.
partiesarrayParties with partially masked names.
qrstringThe public verification page URL.
event

An immutable entry in the audit trail.

event
{
  "id": "evt_2M",
  "object": "event",
  "type": "signer.signed",
  "signature_request": "sr_3n8Kd2Qa1V",
  "signer": "sgr_9fA2",
  "actor": "signer",
  "ip": "197.44.x.x",
  "user_agent": "..",
  "created_at": 1754500000
}
The fieldTypeDescription
typestringThe event type, such as signer.signed.
actorstringowner · signer · system.
ipstringThe actor's IP address.
user_agentstringThe actor's user agent.
webhook_endpoint

A registered URL for receiving events.

webhook_endpoint
{
  "id": "we_1a",
  "object": "webhook_endpoint",
  "url": "https://api.acme.com/hooks/wthaiq",
  "enabled_events": ["signature_request.completed", "signer.signed"],
  "status": "enabled",
  "secret": "whsec_..",
  "created_at": 1754000000
}
The fieldTypeDescription
urlstringThe receiving HTTPS address.
enabled_eventsarrayThe subscribed event types.
statusstringenabled or disabled.
secretstringThe signing secret whsec_ to verify the HMAC.
api_key

An API access key (the full secret is not shown after creation).

api_key
{
  "id": "key_88",
  "object": "api_key",
  "name": "Production",
  "prefix": "sk_9a1b2c3d4e5f6",
  "livemode": true,
  "created_at": 1754000000,
  "last_used_at": 1754500000
}
The fieldTypeDescription
namestringThe key's descriptive name.
prefixstringThe visible prefix of the key.
livemodebooleanWhether the key is live.
last_used_atinteger · nullLast used (Unix timestamp).

Ready to build on Wthaiq?

Start from the quickstart guide and issue your first signature request in minutes — with a small balance and your own email as the recipient of the first test.

Get started in five minutes