This guide takes you step by step from creating an API key to downloading a legally binding signed contract and verifying it — in five direct steps. The examples are ready to copy in cURL, Node, Python and PHP. Note: there is no test mode — every key is live and every call is genuinely billed, so start with a small balance and your own email address as the recipient of the first test.
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}
]
}'
Simple preparation you can finish in a minute, then move straight to your first API call.
Register an account on the platform to reach the dashboard. From there you manage your keys, your templates, your signature requests and your event logs.
Create a key in the format sk_ from the dashboard. It gives you full access to the API — and the key is live from the very first moment, so every call you make with it is real and billed.
Programmatic access is available on the plans that include the API. See Pricing page to choose the plan that suits your usage volume.
sk_ live from the moment it is created: it sends real emails to the signers, issues binding contracts with legal standing, and charges your balance for real (EGP 20 per signature request, plus EGP 35 for each signer who requires identity verification). To try it safely: top up a small balance and use your own email address as the recipient of your first test.Follow the steps in order. Each step stands on its own and comes with copy-ready examples in four languages.
From the dashboard, open Dashboard ← Developers ← API Keys and create a new key. You get two types of key, and there is no test mode — every key works on your real data immediately:
| Key | Description |
|---|---|
| sk_... | A secret key, for the server only, with full privileges. It genuinely sends the messages, issues binding contracts with legal standing, and its cost counts towards your usage from the very first call. |
| pk_... | A publishable key, safe to expose in the browser, locked to your domains (allowed_origins), and limited to a set of paths (templates, signature requests, signers, flows). |
Authentication is performed through the header Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx with every request. Also pin the API version through the header Wthaiq-Version: 2026-07-01 so that the behaviour of the API stays stable across updates.
sk_) is used on the server only. Never place it in a web or mobile app or in a public repository. If it leaks, revoke it immediately from the dashboard and create a new one. See Security guide for the details.Our official first-class libraries are available for Node, Python and PHP, and they handle authentication, retries and API version pinning automatically. Or work with the API directly over cURL, without any package.
# cURL needs no SDK — just set your key
export WTHAIQ_API_KEY="sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Check connectivity by fetching the ready-made templates
curl https://wthaiq.com/api/v1/templates \
-H "Authorization: Bearer $WTHAIQ_API_KEY" \
-H "Wthaiq-Version: 2026-07-01"
npm i @wthaiq/node
import Wthaiq from '@wthaiq/node';
const wt = new Wthaiq('sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
pip install wthaiq
import wthaiq
wt = wthaiq.Client('sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
composer require wthaiq/wthaiq-php
$wt = new \Wthaiq\Client('sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
Now create a signature request from a ready-made template. In the following example we use the employment contract template (tpl_employment), and a single signer who signs by drawing their signature (method: draw) after identity verification is passed (require_identity: true), which raises the request to the advanced signature level (AES).
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"}
}'
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', 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' }
});
console.log(sr.signers[0].signing_url);
sr = wt.signature_requests.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', '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'},
)
print(sr.signers[0].signing_url)
$sr = $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', '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'],
]);
echo $sr->signers[0]->signing_url;
Response is an object signature_request with status sent, and contains the signer together with the signing link signing_url ready to open or embed:
{
"id": "sr_3n8Kd2Qa1V",
"object": "signature_request",
"livemode": false,
"status": "sent",
"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,
"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,
"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..",
"identity": { "status": "pending", "provider": "didit", "level": "aes" }
}
]
}
viewed and partially_signed up to completed.require_identity.WTQ-... automatically when signing completes.There are two ways to know when the other party signs. Choose manual polling for quick experiments, and Webhooks for production.
Method one — polling: Query the request whenever you like. The status status reflects the latest state.
curl https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V \
-H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Wthaiq-Version: 2026-07-01"
signature_request.completed, so you react as soon as signing completes, with no manual polling. Details of registration and signature verification are in The Webhooks page.Method two — Webhook: The full event body reaches you through POST to your endpoint, and the affected resource is wrapped under data.object:
{
"id": "evt_2M",
"object": "event",
"type": "signature_request.completed",
"created_at": 1754500000,
"livemode": false,
"data": {
"object": {
"id": "sr_3n8Kd2Qa1V",
"object": "signature_request",
"status": "completed",
"legal_level": "aes",
"reference": "WTQ-000123",
"completed_at": 1754500000,
"download_url": "https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/download"
}
}
}
Verify the signature header Wthaiq-Signature on every incoming request, and return the response 2xx quickly, and run the heavy work asynchronously.
As soon as the status becomes completed, download the final signed and sealed PDF file. This endpoint is available only for completed requests and returns application/pdf. The independent cryptographic evidence for this path is the sealed evidence record (JSON), not a PAdES signature embedded inside the PDF — download it through GET /api/evidence.php?scope=api&doc=<id>.
curl -L https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/download \
-H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-o contract-signed.pdf
To verify the contract's integrity publicly, use Reference code WTQ-... assigned on completion. Any party holding the reference can confirm the document's authenticity and integrity — programmatically or through the public page /verify.
curl https://wthaiq.com/api/v1/verifications/WTQ-000123 \
-H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Response
{
"object": "verification",
"reference": "WTQ-000123",
"found": true,
"status": "completed",
"integrity": "intact",
"document": { "title": "Employment contract", "sha256": "a3f1..", "format": "pades-lt", "legal_level": "aes" }
}
SHA-256 of the document at sealing time, and the request is sealed with an authenticated evidence record bearing a signature of type Ed25519 (which any party can verify with the public key at /trust) and a certified timestamp to RFC 3161. Any later change to a single byte changes the hash, turning integrity to modified and tampering is detected immediately. More details in The legal standing page.Go deeper into the full reference, secure your system with Webhooks, and extend your integration.
The comprehensive reference for the concepts, authentication, error handling and pagination.
Open the documentationReceive events in real time and verify the signature with the header Wthaiq-Signature.
Every endpoint, object, field and error is documented in detail with live examples.
Browse the referenceOfficial libraries for Node, Python, PHP, Go, Ruby and .NET, with examples.
Choose your libraryConnect Wthaiq to your tools and systems through ready-made integrations and automated workflows.
Browse the integrationsUnderstand the SES, AES and QES levels, the PAdES standards and the Egyptian legal framework.
Learn moreNo. There is no separate test mode — every sk_ Live from the moment it is created: it sends real emails to signers, issues legally effective certificates, and charges your balance with every call. The safe way to test: top up a small balance and use your own email address as the recipient. For browser use without exposing full privileges, use a publishable key pk_ — limited to specific paths and locked to your domains.
You control the level through the two fields legal_level and method. The simple level, SES, is a drawn signature with a verification code sent by email. The advanced level, AES, adds documented identity verification (an official document and a live face match through Didit) that ties the signature to a real person — enable it by setting require_identity: true. The qualified QES level uses a certificate held on a hardware token (method: token) from a licensed certification authority, and carries the highest legal standing under Egyptian Electronic Signature Law No. 15 of 2004. Fuller details in The legal standing page.
Both options are open to you. The easiest is to use signing_url hosted, which is returned with every signer. And if you want an embedded experience under your own brand without leaving your app, request a short-lived signing session via POST /v1/signers/{id}/signing_session and embed it in your interface. See API reference for the details of embedded signing (White-label).
Use the secret key on the server only, and never place it in web or mobile client code or in a public repository. Store it in environment variables or a secrets vault, restrict it to the least possible privilege, and rotate it regularly. If you suspect it has leaked, revoke it immediately from the dashboard and create a new one. Also secure your Webhook endpoints by verifying the header Wthaiq-Signature. See Security guide.
The official first-class libraries are Node.js, Python and PHP, and libraries are also available for Go, Ruby and .NET. All of them are open source under the organisation github.com/wthaiq, follow semantic versioning (SemVer) and pin the API version header automatically. The full list and the installation commands are in The SDK page.
Create your key now and run the full flow in minutes — with a small balance and your own email as the recipient of the first test.