Overview
The Template Model lets you design a document once — in the Sadq portal — and reuse it via the API any time you need that same document signed again. You build the template visually (add text fields the creator will fill in, add signer fields for whoever needs to sign), and from then on you only need to pass a templateId to generate a new envelope. No file upload, no re-specifying field positions on every call.
Key characteristics:
- The document, its text fields, and its signer roles are designed once, visually, in the template editor.
- Each custom field gets a Field Label when you build the template (e.g.
txtName) — that label becomes the key you use later to fill in real data viacreatorFields. - Initiating from a template returns the full
destinationsarray already populated with each signer's field positions, calculated from where you placed them on the PDF — you only need to fill in their contact info before inviting them. - Sandbox OTP for portal login is
1234.
Step 1 — Build the Template (Sadq Portal)
Do this once per document type. You only repeat Steps 2–5 below for every new signing request.
- Log in to the Sadq portal and go to Templates > Upload From Document Templates. Click Create New Template and upload your PDF (or click Edit next to an existing template).
- In the template editor, with Creator selected under Manage Recipients, drag a Text field from the field palette onto the PDF wherever it belongs. In the Field Options panel, set Field Label to a short identifier (e.g.
txtName) — this is the ID you'll use later to fill the field with real data. - Click Manage Recipients > Add External Signer and give the signer a name (e.g.
External_1). They now appear as a recipient alongside Creator. - Select the external signer's tab under Manage Recipients, drag a Signature field onto the PDF where they should sign, and click Save Template.
- Grab the
templateId— either from the editor's URL or viaGET /api/v1/templates.
Note: Repeat steps 2–4 for as many text fields and signers as the document needs. Every custom text field needs its own Field Label; every signer needs at least one field placed for them.
Step 2 — Authenticate
Call this before any other API. Send credentials as application/x-www-form-urlencoded with Basic Auth header. The returned JWT is valid for approximately 27.5 hours (99,200 seconds). Include it as Authorization: Bearer <token> on every subsequent request.
| Method | Endpoint | Purpose |
|---|---|---|
| POST | /Authentication/Authority/Token | Obtain JWT Bearer token |
Step 3 — Initiate the Envelope from the Template
| Method | Endpoint | Content Type |
|---|---|---|
| POST | /api/v1/envelopes/initiate-by-template | multipart/form-data |
Request fields
| Field | Required | Description |
|---|---|---|
| TemplateId | Yes | The template built in Step 1 |
| WebhookId | No | ID of a registered webhook to notify on completion |
| ReferenceNumber | No | Your own tracking reference |
| Password | No | Optional document password protection |
| AllowUserToDownloadDocument | No | Lets the signer download the document from the self-service portal |
Response fields
| Field | Description |
|---|---|
| data.documentId | Unique ID for the document — use this in Send Invitation |
| data.envelopeId | Unique ID for the envelope |
| data.destinations | Every signer you added in the template, with their field positions already calculated — pass back largely unchanged in Step 4 |
| data.creatorFields | One entry per Field Label you set on a text field (e.g. txtName), with an empty value — fill these in before Step 4 |
| data.referenceNumber | Your reference number |
| errorCode | 0 = success, non-zero = error (see Error Codes) |
Note: Unlike the plain upload workflow, you don't define
destinationsor signature positions on this call — they come back pre-built from the template.
Example request
curl -X POST 'https://sandbox-api.sadq-sa.com/api/v1/envelopes/initiate-by-template' \
-H 'Authorization: Bearer {token}' \
-F 'TemplateId=bce3243b-b703-44e2-8a2b-584965bbc6cb'
Example response
{
"data": {
"documentId": "aeb2b9ef-f390-4073-813e-25f7e03a3271",
"envelopeId": "3b3eb464-af37-4408-a6d7-31de7093a77e",
"destinations": [
{
"destinationName": "External_1",
"destinationEmail": null,
"destinationPhoneNumber": "",
"signeOrder": 0,
"signatories": [
{
"positionX": 330.05997,
"positionY": 214.34555,
"signatureHigh": 45.09373,
"signatureWidth": 134.11014,
"pageNumber": 1,
"text": "",
"type": "Signature",
"fieldLabel": null,
"fontFamily": "F772B5DD-B9D3-4C67-B9AA-3ECB22BF3121",
"fontSize": 12,
"appendDate": false,
"documentId": null,
"isRequired": true,
"textAlignment": 0,
"dataSource": null
}
],
"availableTo": "2026-07-18T00:00:00+03:00",
"consentOnly": false,
"authenticationType": 0,
"allowUserToSignAnyWhere": false,
"destinationType": 1,
"nationalId": null,
"invitationLanguage": null,
"redirectUrl": null,
"allowUserToAddDestination": false,
"delegaterId": null,
"commercialNumber": null,
"uploadFile": null,
"dailyNotify": false,
"escalation": null
}
],
"referenceNumber": "Req-A-2026-381",
"creatorFields": {
"txtName": ""
}
},
"errorCode": 0,
"message": "Success",
"returnUrl": "/dashboard",
"stateValidationErrors": null,
"signature": ""
}
Step 4 — Send the Invitation
Take the destinations array from Step 3 as-is, fill in each signer's destinationEmail and/or destinationPhoneNumber (at least one is required), and fill creatorFields with the real values you want printed in the document.
| Method | Endpoint | Key Difference |
|---|---|---|
| POST | /api/v1/invitations | Standard — by documentId. Sadq sends email/SMS. No link in response. |
| POST | /api/v2/invitations | Returns invitationLink in response. No email/SMS sent. |
| POST | /api/v3/invitations | Best of both — Sadq sends email/SMS AND returns invitationLink. |
Request fields
| Field | Required | Description |
|---|---|---|
| DocumentId | Yes | From the Step 3 response |
| Destinations | Yes | Same array returned by Step 3 — filled in with real destinationEmail/destinationPhoneNumber |
| CreatorFields | No | Same keys returned by Step 3 (e.g. txtName) — fill in the real value for each |
| InvitationMessage | No | Custom message shown to the signer |
| InvitationSubject | No | Custom email subject |
| SendUserInvitation | No | Default true — set false to create without notifying yet |
| CustomSmsMessage | No | Custom SMS text if notified by phone |
Example request
{
"documentId": "aeb2b9ef-f390-4073-813e-25f7e03a3271",
"destinations": [
{
"destinationName": "External_1",
"destinationEmail": "ahmed@example.com",
"destinationPhoneNumber": "",
"signeOrder": 0,
"signatories": [
{
"positionX": 330.05997,
"positionY": 214.34555,
"signatureHigh": 45.09373,
"signatureWidth": 134.11014,
"pageNumber": 1,
"type": "Signature",
"isRequired": true
}
],
"authenticationType": 0
}
],
"creatorFields": {
"txtName": "Ahmed Al-Rashid"
}
}
Example response
{
"data": true,
"errorCode": 0,
"message": "Success",
"returnUrl": null,
"stateValidationErrors": null,
"signature": ""
}
Step 5 — Track & Download
Option A — Webhook (recommended)
If WebhookId was set in Step 3, Sadq automatically POSTs to your registered URL when all signers complete or reject, with the signed PDF included as Base64 — no separate download call needed.
Option B — Poll envelope status
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/v1/envelopes/{envelopeId}/status | Get current signing status and per-signer progress |
Download the signed document
| Method | Endpoint | Returns |
|---|---|---|
| GET | /api/v1/documents/{id}/signed | Signed PDF as binary file download |
| GET | /api/v1/documents/{id}/completed/base64 | Signed PDF as Base64 JSON |
Common Errors
Every response includes an errorCode field — 0 means success. The ones most likely to come up in this flow:
| Code | Name | Description |
|---|---|---|
| 3 | UnAuthorized | Token missing or doesn't have permission |
| 8 | ValidationError | Request body failed validation — e.g. missing TemplateId, or a destination with no email and no phone |
| 9 | BadRequest | Malformed or invalid request |
| 10 | InvalidFileIdOrRequestId | The templateId, documentId, or envelopeId doesn't exist |
| 100 | GeneralError | Unexpected server-side error |
Quick Reference
| Method | Endpoint | Step | Purpose |
|---|---|---|---|
| POST | /Authentication/Authority/Token | 2 | Get Bearer token |
| GET | /api/v1/templates | 1 | List your templates |
| POST | /api/v1/envelopes/initiate-by-template | 3 | Create envelope from a template |
| POST | /api/v1/invitations | 4 | Send invitation by documentId |
| GET | /api/v1/envelopes/{envelopeId}/status | 5 | Poll signing status |
| GET | /api/v1/documents/{id}/signed | 5 | Download signed PDF |