openapi: 3.0.3
info:
title: 'Emza Platform — Public API v1'
description: 'Public REST API for Contract creation and Electronic Signature lifecycle. Bilingual (fa/en) errors. RFC 7807 problem+json envelope.'
version: 1.0.0
servers:
-
url: 'https://elemza.com'
tags:
-
name: Contracts
description: ''
-
name: 'Forms — read-only access to v2 forms owned by the authenticated user.'
description: "\nPhase 5 MVP: list + show + stats. Submissions endpoint will land in Phase 5b\nwith proper pagination + transformer."
-
name: 'Meta — health, identity, quota, cost'
description: ''
-
name: Signers
description: "\nSigner endpoints for a contract.\n\nPhase 1 = read-only (index + show). Resend + delete = Phase 2 (mutations).\n\nSpec: docs/api-master-plan/02-endpoint-surface.md (Signer endpoints)."
-
name: Templates
description: "\nTemplate endpoints — read-only in v1.0. CRUD lands in v1.x.\n\nSpec: docs/api-master-plan/02-endpoint-surface.md (Template endpoints)."
-
name: Webhooks
description: "\nOutbound webhook subscriptions. Customers create one or more endpoints, each\nfiltered by an array of event names. Deliveries are signed with HMAC-SHA256\nvia the secret returned at create-time (shown only once).\n\nScope: `webhooks:manage`."
components:
securitySchemes:
default:
type: http
scheme: bearer
description: 'Create your token in the dashboard at /dashboard/api-tokens. The plain token is shown once at creation. Format is {id}|{secret} — send it verbatim in the Authorization header, no pat_live_ prefix.'
security:
-
default: []
paths:
'/api/v1/contracts/{code}/dl':
get:
summary: 'Public signed-URL download.'
operationId: publicSignedURLDownload
description: "Validates Laravel's `signed` middleware. No Bearer token required —\nthe URL signature is the auth proof. The signature is generated by\n`downloadPdf()` after the bearer was verified, so this is functionally\nequivalent to a 24h time-bounded capability URL.\n\nRecords an `ApiRequestLog`-style entry via the underlying `ContractDownload`\naudit table (same trail as the user-panel signed-link download flow)."
parameters: []
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/not_found'
status: 404
code: not_found
title_fa: 'منبع یافت نشد'
title_en: 'Resource not found'
request_id: 01KRHE8WJSA8HZ09HV9GBG6T44
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/not_found'
status:
type: integer
example: 404
code:
type: string
example: not_found
title_fa:
type: string
example: 'منبع یافت نشد'
title_en:
type: string
example: 'Resource not found'
request_id:
type: string
example: 01KRHE8WJSA8HZ09HV9GBG6T44
tags:
- Contracts
parameters:
-
in: path
name: code
description: '16-char public code.'
example: architecto
required: true
schema:
type: string
/api/v1/contracts:
get:
summary: 'List contracts'
operationId: listContracts
description: "Returns paginated contracts visible to caller. Org root sees own + descendants'\nnon-private contracts; non-root sees own + contracts where they're a signer.\n\nScope: `contracts:read`.\n\n**PII filter scope requirement:** filtering by `signer_mobile` or\n`signer_national_code` requires the additional `signers:read_pii` ability\non your token. Without it, those query parameters return\n**403 `scope_missing`**. This prevents a low-privilege `contracts:read`\ntoken from using the index as a confirmation oracle to check whether a\ngiven mobile/national-code signs any contract on the owner's tree.\n\nThe `*` wildcard scope also passes the check."
parameters:
-
in: query
name: status
description: 'Filter by status: draft|waiting_signature|completed|voided|canceled.'
example: waiting_signature
required: false
schema:
type: string
description: 'Filter by status: draft|waiting_signature|completed|voided|canceled.'
example: waiting_signature
-
in: query
name: created_after
description: 'ISO 8601 datetime.'
example: '2026-01-01T00:00:00Z'
required: false
schema:
type: string
description: 'ISO 8601 datetime.'
example: '2026-01-01T00:00:00Z'
-
in: query
name: created_before
description: 'ISO 8601 datetime.'
example: architecto
required: false
schema:
type: string
description: 'ISO 8601 datetime.'
example: architecto
-
in: query
name: signer_mobile
description: 'Iran mobile regex 09XXXXXXXXX. **Requires `signers:read_pii` scope.**'
example: '09121234567'
required: false
schema:
type: string
description: 'Iran mobile regex 09XXXXXXXXX. **Requires `signers:read_pii` scope.**'
example: '09121234567'
-
in: query
name: signer_national_code
description: '10-digit Iran national code. **Requires `signers:read_pii` scope.**'
example: architecto
required: false
schema:
type: string
description: '10-digit Iran national code. **Requires `signers:read_pii` scope.**'
example: architecto
-
in: query
name: template_id
description: 'Filter by template.'
example: 16
required: false
schema:
type: integer
description: 'Filter by template.'
example: 16
-
in: query
name: is_private
description: 'Filter by privacy flag.'
example: false
required: false
schema:
type: boolean
description: 'Filter by privacy flag.'
example: false
-
in: query
name: page
description: 'Default 1.'
example: 16
required: false
schema:
type: integer
description: 'Default 1.'
example: 16
-
in: query
name: per_page
description: 'Max 100. Default 20.'
example: 16
required: false
schema:
type: integer
description: 'Max 100. Default 20.'
example: 16
-
in: query
name: sort
description: 'created_at | -created_at | completed_at | -completed_at. Default -created_at.'
example: architecto
required: false
schema:
type: string
description: 'created_at | -created_at | completed_at | -completed_at. Default -created_at.'
example: architecto
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WM9R0KTDR3ECFDY2JD0
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WM9R0KTDR3ECFDY2JD0
403:
description: 'PII filter without scope'
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/scope_missing'
status: 403
code: scope_missing
title_fa: 'این فیلتر نیاز به مجوز signers:read_pii دارد'
title_en: 'This filter requires the signers:read_pii scope'
request_id: 01KRH8JRC4Y855P10CYC1C0AYS
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/scope_missing'
status:
type: integer
example: 403
code:
type: string
example: scope_missing
title_fa:
type: string
example: 'این فیلتر نیاز به مجوز signers:read_pii دارد'
title_en:
type: string
example: 'This filter requires the signers:read_pii scope'
request_id:
type: string
example: 01KRH8JRC4Y855P10CYC1C0AYS
tags:
- Contracts
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
status:
type: string
description: ''
example: draft
enum:
- draft
- waiting_signature
- completed
- voided
- canceled
nullable: true
created_after:
type: string
description: 'value یک تاریخ معتبر نیست.'
example: '2026-05-13T23:20:57'
nullable: true
created_before:
type: string
description: 'value یک تاریخ معتبر نیست.'
example: '2026-05-13T23:20:57'
nullable: true
signer_mobile:
type: string
description: 'Must match the regex /^09[0-9]{9}$/.'
example: '09564255931'
nullable: true
signer_national_code:
type: string
description: 'value باید 10 کاراکتر باشد.'
example: ikhwaykcmy
nullable: true
template_id:
type: integer
description: ''
example: 16
nullable: true
is_private:
type: boolean
description: ''
example: false
nullable: true
page:
type: integer
description: 'value باید حداقل 1 باشد.'
example: 22
nullable: true
per_page:
type: integer
description: 'value باید حداقل 1 باشد. value نباید بیشتر از 100 باشد.'
example: 7
nullable: true
sort:
type: string
description: ''
example: completed_at
enum:
- created_at
- '-created_at'
- completed_at
- '-completed_at'
nullable: true
post:
summary: 'Create contract'
operationId: createContract
description: "Creates a new contract. Two modes:\n\n**One-shot** — multipart/form-data with `file` field + metadata. File is\npersisted to staging and `ProcessContractFilesJob` is dispatched. Optional\n`signers[]` are added by the same job after page rasterization completes.\nResponse is **202 Accepted**; poll `_links.processing` until `ready=true`.\n\n**Multi-step** — JSON without `file`. Creates a `draft` contract row only.\nCaller then issues `POST /contracts/{code}/files` to attach the document.\n\nScope: `contracts:write`."
parameters: []
responses: { }
tags:
- Contracts
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
title:
type: string
description: 'Contract title (1..120 chars).'
example: 'قرارداد بیمه شخص ثالث'
all_pages_signature:
type: boolean
description: 'Default false. When true, every page must carry the signature element.'
example: false
is_private:
type: boolean
description: 'Default false. When true, hides contract from org-ancestors.'
example: false
template_id:
type: integer
description: 'Optional — must be owned by caller or shared from org root.'
example: 16
template_enforcement:
type: string
description: 'strict|loose. Optional — only meaningful when template_id is set.'
example: architecto
file:
type: string
format: binary
description: 'PDF or PNG/JPG, max 4MB (multipart/form-data only).'
signers:
type: array
description: 'Optional inline signers (one-shot). Same shape as POST /contracts/{code}/signers.'
example:
- architecto
items:
type: string
required:
- title
'/api/v1/contracts/{code}':
get:
summary: 'Get contract'
operationId: getContract
description: "Returns full contract shape with embedded signers, per-page geometry,\nand template positions snapshot.\n\nScope: `contracts:read`."
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WMFTGBCSPQD772SFBS2
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WMFTGBCSPQD772SFBS2
tags:
- Contracts
delete:
summary: 'Cancel contract'
operationId: cancelContract
description: "Cancels a contract in `draft` or `waiting_signature` state. Refunds the\nbilled amount to the original wallet (100% if no signer signed, 50% if any\nShahkar was consumed). Deletes contract files from storage. Waiting signers\nreceive an SMS notifying them the contract was canceled.\n\nScope: `contracts:write`."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
code: ABC1234567XYZ890
status: canceled
canceled_at: '2026-05-11T10:00:00.000Z'
refund:
amount: 60000
currency: IRT
properties:
data:
type: object
properties:
code:
type: string
example: ABC1234567XYZ890
status:
type: string
example: canceled
canceled_at:
type: string
example: '2026-05-11T10:00:00.000Z'
refund:
type: object
properties:
amount:
type: integer
example: 60000
currency:
type: string
example: IRT
tags:
- Contracts
parameters:
-
in: path
name: code
description: '16-char public code (A-Z, 0-9).'
example: ABC1234567XYZ890
required: true
schema:
type: string
'/api/v1/contracts/{code}/pdf':
get:
summary: 'Download signed PDF'
operationId: downloadSignedPDF
description: "Returns the final signed PDF in one of three formats (Phase 3):\n\n- `format=redirect` (default) — `302 Found` to a signed URL (24h TTL).\n The signed URL points at the unauthenticated `/api/v1/contracts/{code}/dl`\n route, which validates the signature and streams the file. Use this when\n embedding in `` for partner-facing UIs.\n- `format=url` — JSON envelope with `url`, `expires_at`, `filename`, `size_bytes`,\n `checksum_sha256`. Use this when the API client wants to hand the URL\n to another system, embed in email, or pre-flight before downloading.\n- `format=stream` — streams the PDF inline (consumes API egress). Use when\n the caller is a backend service that wants the bytes in one round-trip.\n\nScope: `contracts:read`."
parameters:
-
in: query
name: format
description: 'redirect|url|stream. Default redirect.'
example: url
required: false
schema:
type: string
description: 'redirect|url|stream. Default redirect.'
example: url
-
in: query
name: ttl
description: 'Signed URL lifetime in seconds (60..86400). Default 86400. Ignored when `format=stream`.'
example: 16
required: false
schema:
type: integer
description: 'Signed URL lifetime in seconds (60..86400). Default 86400. Ignored when `format=stream`.'
example: 16
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/not_found'
status: 404
code: not_found
title_fa: 'منبع یافت نشد'
title_en: 'Resource not found'
request_id: 01KRHE8WN0N5RGPWKT3GBWRRXB
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/not_found'
status:
type: integer
example: 404
code:
type: string
example: not_found
title_fa:
type: string
example: 'منبع یافت نشد'
title_en:
type: string
example: 'Resource not found'
request_id:
type: string
example: 01KRHE8WN0N5RGPWKT3GBWRRXB
tags:
- Contracts
parameters:
-
in: path
name: code
description: '16-char public code.'
example: architecto
required: true
schema:
type: string
'/api/v1/contracts/{code}/processing':
get:
summary: 'Poll processing status'
operationId: pollProcessingStatus
description: "Lightweight (<200 bytes) status of the rasterization + signer-creation job.\nReads `contract:progress:{id}` cache key directly — no DB hit.\n\n**Step values:** `files`, `signers`, `sms`, `done`, `error`.\n\nReturns `ready=true` when `step=done`. If progress key is gone (TTL expired)\nand contract has signers + pages, falls back to `ready=true` (job finished\nearlier and key was reaped).\n\nScope: `contracts:read`."
parameters: []
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/not_found'
status: 404
code: not_found
title_fa: 'منبع یافت نشد'
title_en: 'Resource not found'
request_id: 01KRHE8WNFY730DN7F9DZA757Z
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/not_found'
status:
type: integer
example: 404
code:
type: string
example: not_found
title_fa:
type: string
example: 'منبع یافت نشد'
title_en:
type: string
example: 'Resource not found'
request_id:
type: string
example: 01KRHE8WNFY730DN7F9DZA757Z
tags:
- Contracts
parameters:
-
in: path
name: code
description: '16-char contract code.'
example: architecto
required: true
schema:
type: string
'/api/v1/contracts/{code}/files':
post:
summary: 'Upload contract file(s) (two-step flow)'
operationId: uploadContractFilestwoStepFlow
description: "Attaches a file to an existing **draft** contract that has no pages yet.\nSame async pipeline as `POST /contracts` one-shot: stages bytes, dispatches\n`ProcessContractFilesJob`, returns 202 with the `processing` link.\n\nScope: `contracts:write`."
parameters: []
responses: { }
tags:
- Contracts
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: 'PDF or PNG/JPG, max 4MB (multipart/form-data).'
required:
- file
parameters:
-
in: path
name: code
description: '16-char contract code.'
example: architecto
required: true
schema:
type: string
/api/v1/forms:
get:
summary: "List forms (v2 only — schema_version='2.0')"
operationId: listFormsv2OnlySchemaVersion20
description: "Returns up to 100 most recent v2-schema forms owned by the calling user.\nForm Builder v1 records are excluded — use the dashboard for those.\n\nScope required: `forms:read`."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 142
slug: frm-e7465df28dd45785
title: 'قرارداد همکاری'
status: published
renderer_type: online
submission_count: 23
views_count: 187
created_at: '2026-05-10T14:32:00.000Z'
properties:
data:
type: array
example:
-
id: 142
slug: frm-e7465df28dd45785
title: 'قرارداد همکاری'
status: published
renderer_type: online
submission_count: 23
views_count: 187
created_at: '2026-05-10T14:32:00.000Z'
items:
type: object
properties:
id:
type: integer
example: 142
slug:
type: string
example: frm-e7465df28dd45785
title:
type: string
example: 'قرارداد همکاری'
status:
type: string
example: published
renderer_type:
type: string
example: online
submission_count:
type: integer
example: 23
views_count:
type: integer
example: 187
created_at:
type: string
example: '2026-05-10T14:32:00.000Z'
tags:
- 'Forms — read-only access to v2 forms owned by the authenticated user.'
'/api/v1/forms/{slug}':
get:
summary: 'Show one form (metadata only — schema available via dedicated /schema endpoint in future)'
operationId: showOneFormmetadataOnlySchemaAvailableViaDedicatedschemaEndpointInFuture
description: 'Scope required: `forms:read`.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
id: 142
slug: frm-e7465df28dd45785
title: 'قرارداد همکاری'
status: published
renderer_type: online
submission_count: 23
views_count: 187
created_at: '2026-05-10T14:32:00.000Z'
properties:
data:
type: object
properties:
id:
type: integer
example: 142
slug:
type: string
example: frm-e7465df28dd45785
title:
type: string
example: 'قرارداد همکاری'
status:
type: string
example: published
renderer_type:
type: string
example: online
submission_count:
type: integer
example: 23
views_count:
type: integer
example: 187
created_at:
type: string
example: '2026-05-10T14:32:00.000Z'
404:
description: 'Not found or not owned'
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/form_not_found'
status: 404
code: form_not_found
title_fa: 'فرم یافت نشد یا متعلق به شما نیست'
title_en: 'Form not found or not owned by the caller'
request_id: 01KRH8JRC4Y855P10CYC1C0AYS
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/form_not_found'
status:
type: integer
example: 404
code:
type: string
example: form_not_found
title_fa:
type: string
example: 'فرم یافت نشد یا متعلق به شما نیست'
title_en:
type: string
example: 'Form not found or not owned by the caller'
request_id:
type: string
example: 01KRH8JRC4Y855P10CYC1C0AYS
tags:
- 'Forms — read-only access to v2 forms owned by the authenticated user.'
parameters:
-
in: path
name: slug
description: "The form's public slug."
example: frm-e7465df28dd45785
required: true
schema:
type: string
'/api/v1/forms/{slug}/stats':
get:
summary: 'Form analytics (views/starts/submissions/conversion rates).'
operationId: formAnalyticsviewsstartssubmissionsconversionRates
description: "Aggregated counters returned by `FormAnalyticsService::stats()`. Includes\nlifetime totals plus 7-day and 30-day windows so you can chart trends without\nadditional queries.\n\nScope required: `forms:read`."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
totals:
views: 187
starts: 41
submissions: 23
conversion_rate: 56.1
last_7_days:
views: 32
starts: 9
submissions: 4
last_30_days:
views: 154
starts: 38
submissions: 21
properties:
data:
type: object
properties:
totals:
type: object
properties:
views:
type: integer
example: 187
starts:
type: integer
example: 41
submissions:
type: integer
example: 23
conversion_rate:
type: number
example: 56.1
last_7_days:
type: object
properties:
views:
type: integer
example: 32
starts:
type: integer
example: 9
submissions:
type: integer
example: 4
last_30_days:
type: object
properties:
views:
type: integer
example: 154
starts:
type: integer
example: 38
submissions:
type: integer
example: 21
404:
description: 'Not found or not owned'
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/form_not_found'
status: 404
code: form_not_found
title_fa: 'فرم یافت نشد یا متعلق به شما نیست'
title_en: 'Form not found or not owned by the caller'
request_id: 01KRH8JRC4Y855P10CYC1C0AYS
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/form_not_found'
status:
type: integer
example: 404
code:
type: string
example: form_not_found
title_fa:
type: string
example: 'فرم یافت نشد یا متعلق به شما نیست'
title_en:
type: string
example: 'Form not found or not owned by the caller'
request_id:
type: string
example: 01KRH8JRC4Y855P10CYC1C0AYS
tags:
- 'Forms — read-only access to v2 forms owned by the authenticated user.'
parameters:
-
in: path
name: slug
description: "The form's public slug."
example: frm-e7465df28dd45785
required: true
schema:
type: string
/api/v1/health:
get:
summary: 'Health check'
operationId: healthCheck
description: "Lightweight liveness probe. **No authentication required.**\nAlways returns 200 if the API process is up."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
status: ok
time: '2026-05-11T11:53:29.399Z'
version: v1.0.0
properties:
status:
type: string
example: ok
time:
type: string
example: '2026-05-11T11:53:29.399Z'
version:
type: string
example: v1.0.0
tags:
- 'Meta — health, identity, quota, cost'
security: []
/api/v1/me:
get:
summary: 'Caller identity'
operationId: callerIdentity
description: "Returns information about the authenticated token, its owner user, and\nthe billing context (wallet balance, org root). Any valid token can call this —\nno specific scope required.\n\nUse the `token.mode` field to confirm whether your token is in **live** mode\n(charges your wallet, real KYC/SMS) or **test** sandbox mode (zero wallet\nimpact, mocked side effects). `wallet_balance` is always in tomans (IRR/10)."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Live token'
type: object
example:
data:
token:
id: '42'
name: production-server-2026
mode: live
scopes:
- 'contracts:read'
- 'contracts:write'
- 'webhooks:manage'
ip_whitelist:
- 203.0.113.5/32
expires_at: '2027-01-01T00:00:00.000Z'
last_used_at: '2026-05-13T18:00:42.000Z'
user:
id: 1234
name: 'آرش بنائیان چمله'
type: personal
is_org_root: false
org_root_id: null
billing_actor_user_id: 1234
wallet_balance: 849310
properties:
data:
type: object
properties:
token:
type: object
properties:
id:
type: string
example: '42'
name:
type: string
example: production-server-2026
mode:
type: string
example: live
description: 'Either `live` or `test`. Sandbox tokens never debit the wallet.'
scopes:
type: array
example:
- 'contracts:read'
- 'contracts:write'
- 'webhooks:manage'
description: 'of granted abilities. `["*"]` means full access.'
items:
type: string
ip_whitelist:
type: array
example:
- 203.0.113.5/32
description: 'If non-empty, requests from other IPs are rejected with 403 `ip_not_allowed`. Five failures within 1h auto-revoke the token.'
items:
type: string
expires_at:
type: string
example: '2027-01-01T00:00:00.000Z'
last_used_at:
type: string
example: '2026-05-13T18:00:42.000Z'
user:
type: object
properties:
id:
type: integer
example: 1234
name:
type: string
example: 'آرش بنائیان چمله'
type:
type: string
example: personal
is_org_root:
type: boolean
example: false
description: 'True when this user is the root of a Sub-Organization tree (can invite sub-users).'
org_root_id:
type: string
example: null
description: 'ID of the org root if this user is a member, else null.'
billing_actor_user_id:
type: integer
example: 1234
description: 'The wallet that pays for this request. Same as `user.id` for solo users; may be `org_root_id` for org members with `auto_charge_from_parent=true`.'
wallet_balance:
type: integer
example: 849310
description: 'Current wallet balance in tomans (Iranian Rials ÷ 10).'
-
description: 'Sandbox/test token'
type: object
example:
data:
token:
id: '13'
name: ci-tests-sandbox
mode: test
scopes:
- '*'
ip_whitelist: []
expires_at: null
last_used_at: '2026-05-13T17:52:23.000Z'
user:
id: 1234
name: 'آرش بنائیان چمله'
type: personal
is_org_root: false
org_root_id: null
billing_actor_user_id: 1234
wallet_balance: 849310
properties:
data:
type: object
properties:
token:
type: object
properties:
id:
type: string
example: '13'
name:
type: string
example: ci-tests-sandbox
mode:
type: string
example: test
description: 'Either `live` or `test`. Sandbox tokens never debit the wallet.'
scopes:
type: array
example:
- '*'
description: 'of granted abilities. `["*"]` means full access.'
items:
type: string
ip_whitelist:
type: array
example: []
description: 'If non-empty, requests from other IPs are rejected with 403 `ip_not_allowed`. Five failures within 1h auto-revoke the token.'
expires_at:
type: string
example: null
nullable: true
last_used_at:
type: string
example: '2026-05-13T17:52:23.000Z'
user:
type: object
properties:
id:
type: integer
example: 1234
name:
type: string
example: 'آرش بنائیان چمله'
type:
type: string
example: personal
is_org_root:
type: boolean
example: false
description: 'True when this user is the root of a Sub-Organization tree (can invite sub-users).'
org_root_id:
type: string
example: null
description: 'ID of the org root if this user is a member, else null.'
billing_actor_user_id:
type: integer
example: 1234
description: 'The wallet that pays for this request. Same as `user.id` for solo users; may be `org_root_id` for org members with `auto_charge_from_parent=true`.'
wallet_balance:
type: integer
example: 849310
description: 'Current wallet balance in tomans (Iranian Rials ÷ 10).'
tags:
- 'Meta — health, identity, quota, cost'
/api/v1/quota:
get:
summary: 'Current quota'
operationId: currentQuota
description: "Returns the caller's current-month signature quota usage + remaining,\npackage tier, and today's Shahkar verification credits.\n\n**Quota semantics:**\n- `free_quota` is the monthly allowance from the user's active package\n (Free=3, Bronze=30, Silver=100, Gold=200). Returns 0 if the package\n is expired or unset.\n- `used` is total signatures recorded since `package_started_at` (so\n pre-purchase usage doesn't deplete a freshly-bought plan).\n- `remaining = free_quota - used` for limited plans. **null** means\n genuinely unlimited (Gold-tier `signature_limit` is NULL).\n- When `remaining` reaches 0, signatures still work — they're billed\n as overage from the wallet at the per-unit tariff rate minus the\n package discount (Free=0%, Bronze=20%, Silver=35%, Gold=50%).\n\n**Shahkar credits** are separate from signature quota. Daily allowance\nby package (Free=3, Bronze=5, Silver=10, Gold=20). Once exhausted, the\nclient must purchase batch top-ups before further Shahkar verifications.\n\nScope required: `contracts:read`."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Bronze user mid-month'
type: object
example:
data:
service: electronic_signature
period_start: '2026-05-01T00:00:00.000Z'
period_end: '2026-05-31T23:59:59.999Z'
package:
slug: bronze
name: برنزی
free_quota: 30
used: 17
remaining: 13
shahkar:
free_limit: 5
used_today: 2
free_remaining: 3
extra_credits: 0
extra_remaining: 0
allowed: true
properties:
data:
type: object
properties:
service:
type: string
example: electronic_signature
period_start:
type: string
example: '2026-05-01T00:00:00.000Z'
period_end:
type: string
example: '2026-05-31T23:59:59.999Z'
package:
type: object
properties:
slug:
type: string
example: bronze
name:
type: string
example: برنزی
free_quota:
type: integer
example: 30
description: 'Monthly signature allowance from active package. Zero if package expired or none.'
used:
type: integer
example: 17
description: 'Signatures recorded this period (since package_started_at).'
remaining:
type: integer
example: 13
description: 'Signatures left this period. `null` means unlimited (Gold).'
shahkar:
type: object
properties:
free_limit:
type: integer
example: 5
used_today:
type: integer
example: 2
free_remaining:
type: integer
example: 3
extra_credits:
type: integer
example: 0
extra_remaining:
type: integer
example: 0
allowed:
type: boolean
example: true
description: 'Whether the user may attempt a Shahkar verification right now. False = quota exhausted, must purchase batch top-up.'
-
description: 'Gold (unlimited)'
type: object
example:
data:
service: electronic_signature
period_start: '2026-05-01T00:00:00.000Z'
period_end: '2026-05-31T23:59:59.999Z'
package:
slug: gold
name: طلایی
free_quota: 0
used: 412
remaining: null
shahkar:
free_limit: 20
used_today: 4
free_remaining: 16
extra_credits: 0
extra_remaining: 0
allowed: true
properties:
data:
type: object
properties:
service:
type: string
example: electronic_signature
period_start:
type: string
example: '2026-05-01T00:00:00.000Z'
period_end:
type: string
example: '2026-05-31T23:59:59.999Z'
package:
type: object
properties:
slug:
type: string
example: gold
name:
type: string
example: طلایی
free_quota:
type: integer
example: 0
description: 'Monthly signature allowance from active package. Zero if package expired or none.'
used:
type: integer
example: 412
description: 'Signatures recorded this period (since package_started_at).'
remaining:
type: string
example: null
description: 'Signatures left this period. `null` means unlimited (Gold).'
shahkar:
type: object
properties:
free_limit:
type: integer
example: 20
used_today:
type: integer
example: 4
free_remaining:
type: integer
example: 16
extra_credits:
type: integer
example: 0
extra_remaining:
type: integer
example: 0
allowed:
type: boolean
example: true
description: 'Whether the user may attempt a Shahkar verification right now. False = quota exhausted, must purchase batch top-up.'
-
description: 'No active package (pay-per-use)'
type: object
example:
data:
service: electronic_signature
period_start: '2026-05-01T00:00:00.000Z'
period_end: '2026-05-31T23:59:59.999Z'
package: null
free_quota: 0
used: 0
remaining: 0
shahkar:
free_limit: 0
used_today: 0
free_remaining: 0
extra_credits: 0
extra_remaining: 0
allowed: false
properties:
data:
type: object
properties:
service:
type: string
example: electronic_signature
period_start:
type: string
example: '2026-05-01T00:00:00.000Z'
period_end:
type: string
example: '2026-05-31T23:59:59.999Z'
package:
type: string
example: null
nullable: true
free_quota:
type: integer
example: 0
description: 'Monthly signature allowance from active package. Zero if package expired or none.'
used:
type: integer
example: 0
description: 'Signatures recorded this period (since package_started_at).'
remaining:
type: integer
example: 0
description: 'Signatures left this period. `null` means unlimited (Gold).'
shahkar:
type: object
properties:
free_limit:
type: integer
example: 0
used_today:
type: integer
example: 0
free_remaining:
type: integer
example: 0
extra_credits:
type: integer
example: 0
extra_remaining:
type: integer
example: 0
allowed:
type: boolean
example: false
description: 'Whether the user may attempt a Shahkar verification right now. False = quota exhausted, must purchase batch top-up.'
tags:
- 'Meta — health, identity, quota, cost'
/api/v1/cost-estimate:
get:
summary: 'Cost estimate'
operationId: costEstimate
description: "Returns full cost breakdown for a hypothetical contract without committing.\nUse before calling `POST /contracts` so users can see \"X toman\" upfront.\n\nScope required: `contracts:read`."
parameters:
-
in: query
name: signers
description: 'Number of signers (1-20).'
example: 3
required: true
schema:
type: integer
description: 'Number of signers (1-20).'
example: 3
-
in: query
name: discount_code
description: 'Optional discount code.'
example: WELCOME10
required: false
schema:
type: string
description: 'Optional discount code.'
example: WELCOME10
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WKH1YB2NTD6D607WV75
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WKH1YB2NTD6D607WV75
tags:
- 'Meta — health, identity, quota, cost'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
signers:
type: integer
description: 'value باید حداقل 1 باشد. value نباید بیشتر از 20 باشد.'
example: 16
discount_code:
type: string
description: 'value نباید بیشتر از 32 کاراکتر باشد.'
example: 'n'
nullable: true
required:
- signers
'/api/v1/contracts/{code}/signers':
get:
summary: 'List signers'
operationId: listSigners
description: "Lists all signers on a contract. PII (mobile, national_code) is masked\nunless the caller's token has `signers:read_pii` scope.\n\nScope: `contracts:read`."
parameters: []
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/not_found'
status: 404
code: not_found
title_fa: 'منبع یافت نشد'
title_en: 'Resource not found'
request_id: 01KRHE8WP548K9PK9DM360HMFY
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/not_found'
status:
type: integer
example: 404
code:
type: string
example: not_found
title_fa:
type: string
example: 'منبع یافت نشد'
title_en:
type: string
example: 'Resource not found'
request_id:
type: string
example: 01KRHE8WP548K9PK9DM360HMFY
tags:
- Signers
post:
summary: 'Add signers'
operationId: addSigners
description: "Adds one or more signers to a draft contract. Runs **mandatory** Shahkar\npre-validation (mobile ↔ national_code match) per signer in production.\nOn success, contract transitions to `waiting_signature` and SMS dispatch\njobs are queued for first-order signers.\n\nScope: `signers:write`."
parameters: []
responses: { }
tags:
- Signers
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
signers:
type: array
description: 'Array of signer objects.'
example:
- architecto
items:
type: string
required:
- signers
parameters:
-
in: path
name: code
description: '16-char contract code.'
example: architecto
required: true
schema:
type: string
'/api/v1/contracts/{code}/signers/{slug}':
get:
summary: 'Get signer'
operationId: getSigner
description: "Detailed view of a single signer. PII masked unless token has `signers:read_pii`.\n\nScope: `contracts:read`."
parameters: []
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/not_found'
status: 404
code: not_found
title_fa: 'منبع یافت نشد'
title_en: 'Resource not found'
request_id: 01KRHE8WPK8E5KD8HM7Z66FTF9
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/not_found'
status:
type: integer
example: 404
code:
type: string
example: not_found
title_fa:
type: string
example: 'منبع یافت نشد'
title_en:
type: string
example: 'Resource not found'
request_id:
type: string
example: 01KRHE8WPK8E5KD8HM7Z66FTF9
tags:
- Signers
delete:
summary: 'Remove signer'
operationId: removeSigner
description: "Removes a signer from a contract. Only allowed when:\n- Signer status is `waiting` (not yet signed)\n- Contract status is `draft` or `waiting_signature`\n\nScope: `signers:write`."
parameters: []
responses:
204:
description: ''
content:
application/json:
schema:
type: object
example: { }
properties: { }
tags:
- Signers
parameters:
-
in: path
name: code
description: '16-char contract code.'
example: architecto
required: true
schema:
type: string
-
in: path
name: slug
description: 'Per-signer slug.'
example: DEF456GHJ789KLM
required: true
schema:
type: string
'/api/v1/contracts/{code}/signers/{slug}/resend':
post:
summary: 'Resend signing link SMS'
operationId: resendSigningLinkSMS
description: "Re-sends the signing-link SMS to a waiting signer. Rate-limited to 3 per\n5 minutes per signer (returns 429 `rate_limit_exceeded`).\n\nScope: `signers:write`."
parameters: []
responses: { }
tags:
- Signers
parameters:
-
in: path
name: code
description: '16-char contract code.'
example: architecto
required: true
schema:
type: string
-
in: path
name: slug
description: 'Per-signer slug.'
example: architecto
required: true
schema:
type: string
/api/v1/templates:
get:
summary: 'List templates'
operationId: listTemplates
description: "Lists contract templates the caller can use.\n\n`view_scope` options:\n- `self` — own templates only (default)\n- `shared_from_parent` — own + templates shared by org parent/root\n- `all_org` — entire org tree (org root only)\n\nScope: `templates:read`."
parameters:
-
in: query
name: view_scope
description: 'self | shared_from_parent | all_org. Default self.'
example: architecto
required: false
schema:
type: string
description: 'self | shared_from_parent | all_org. Default self.'
example: architecto
-
in: query
name: active
description: 'Filter by active status.'
example: false
required: false
schema:
type: boolean
description: 'Filter by active status.'
example: false
-
in: query
name: page
description: ''
example: 16
required: false
schema:
type: integer
description: ''
example: 16
-
in: query
name: per_page
description: 'Max 100. Default 20.'
example: 16
required: false
schema:
type: integer
description: 'Max 100. Default 20.'
example: 16
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WQ6V27R11FAYX547415
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WQ6V27R11FAYX547415
tags:
- Templates
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
view_scope:
type: string
description: ''
example: self
enum:
- self
- shared_from_parent
- all_org
nullable: true
active:
type: boolean
description: ''
example: false
nullable: true
page:
type: integer
description: 'value باید حداقل 1 باشد.'
example: 16
nullable: true
per_page:
type: integer
description: 'value باید حداقل 1 باشد. value نباید بیشتر از 100 باشد.'
example: 22
nullable: true
'/api/v1/templates/{id}':
get:
summary: 'Get template'
operationId: getTemplate
description: "Full template shape with embedded positions + page geometry + sample preview link.\n\nScope: `templates:read`."
parameters: []
responses:
404:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/not_found'
status: 404
code: not_found
title_fa: 'منبع یافت نشد'
title_en: 'Resource not found'
request_id: 01KRHE8WQQXR58JHNF0PJNYJJ8
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/not_found'
status:
type: integer
example: 404
code:
type: string
example: not_found
title_fa:
type: string
example: 'منبع یافت نشد'
title_en:
type: string
example: 'Resource not found'
request_id:
type: string
example: 01KRHE8WQQXR58JHNF0PJNYJJ8
tags:
- Templates
parameters:
-
in: path
name: id
description: 'The ID of the template.'
example: architecto
required: true
schema:
type: string
-
in: path
name: template
description: 'Template ID.'
example: 16
required: true
schema:
type: integer
'/api/v1/templates/{template}/preview/{page}':
get:
summary: 'Template page preview'
operationId: templatePagePreview
description: "Returns a signed URL to a single page preview image (PNG).\n**Phase 1 stub** — `url` is null until Phase 3 wires Storage::temporaryUrl().\n\nScope: `templates:read`."
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WQWKZPK2WHZZ35QRQMY
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WQWKZPK2WHZZ35QRQMY
tags:
- Templates
parameters:
-
in: path
name: template
description: 'Template ID.'
example: 16
required: true
schema:
type: integer
-
in: path
name: page
description: 'Page number (1-indexed).'
example: 16
required: true
schema:
type: integer
/api/v1/webhooks:
get:
summary: 'List webhook subscriptions'
operationId: listWebhookSubscriptions
description: ''
parameters:
-
in: query
name: active
description: 'Filter by active state.'
example: false
required: false
schema:
type: boolean
description: 'Filter by active state.'
example: false
-
in: query
name: page
description: ''
example: 16
required: false
schema:
type: integer
description: ''
example: 16
-
in: query
name: per_page
description: 'Max 100.'
example: 16
required: false
schema:
type: integer
description: 'Max 100.'
example: 16
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WRYSR7YDSV5XGGW83X3
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WRYSR7YDSV5XGGW83X3
tags:
- Webhooks
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
active:
type: boolean
description: ''
example: true
nullable: true
page:
type: integer
description: 'value باید حداقل 1 باشد.'
example: 16
nullable: true
per_page:
type: integer
description: 'value باید حداقل 1 باشد. value نباید بیشتر از 100 باشد.'
example: 22
nullable: true
post:
summary: 'Create webhook subscription'
operationId: createWebhookSubscription
description: "Registers an HTTPS endpoint to receive event deliveries. We POST each\nmatching event to your URL with an HMAC-SHA256 signature in the\n`X-Emza-Signature: t=,v1=` header. Compute the signature over\n`\"{t}.{raw_body}\"` using the secret returned at creation.\n\n**The plain signing `secret` is returned ONCE — store it immediately.**\nSubsequent reads (`index`, `show`) omit the secret entirely. Use\n`PATCH /webhooks/{id}` with `rotate_secret=true` to issue a fresh secret\n(invalidates the previous one immediately).\n\n**URL validation (SSRF guard):** the `url` field is rejected with `422`\nif it fails any of:\n - Non-HTTPS scheme (`http://`, `ftp://`, `javascript:`, `file://`)\n - Loopback IPs (`127.0.0.0/8`, `::1`)\n - RFC 1918 private (`10/8`, `172.16/12`, `192.168/16`)\n - Link-local + cloud metadata (`169.254.0.0/16`, `100.100.100.200`)\n - IPv6 link-local (`fe80::/10`) or unique-local (`fc00::/7`)\n - Hostname suffix `.local`, `.internal`, `.private`, `.lan` or `localhost`\n - Hostname whose DNS resolves to any of the above\n\n**Allowed event names** (set in `events` array — use `[\"*\"]` for all):\n`contract.created`, `contract.processing.completed`, `contract.processing.failed`,\n`contract.signer.added`, `contract.signer.authenticated`, `contract.signer.signed`,\n`contract.signer.rejected`, `contract.completed`, `contract.canceled`,\n`contract.refunded`, `*` (wildcard).\n\nIf `*` is present alongside specific events, the specifics are dropped\n(the wildcard makes them redundant). Same event listed twice is deduped.\n\nScope: `webhooks:manage`. Idempotency-Key required (24h replay window)."
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
data:
id: 01krh7z0e8a4n39gf2ary73xbj
url: 'https://example.com/webhooks/emza'
events:
- contract.completed
- contract.signer.signed
active: true
disabled_at: null
failed_deliveries_count: 0
last_delivery_at: null
last_delivery_status: null
created_at: '2026-05-13T18:00:42.000Z'
updated_at: '2026-05-13T18:00:42.000Z'
secret: 35ada3b45aa00df901b74b2b483114315a0c21f7c096f17e0bb29ab7d907fe32
_warning: 'Save this secret now — it will NOT be shown again.'
properties:
data:
type: object
properties:
id:
type: string
example: 01krh7z0e8a4n39gf2ary73xbj
url:
type: string
example: 'https://example.com/webhooks/emza'
events:
type: array
example:
- contract.completed
- contract.signer.signed
items:
type: string
active:
type: boolean
example: true
disabled_at:
type: string
example: null
nullable: true
failed_deliveries_count:
type: integer
example: 0
last_delivery_at:
type: string
example: null
nullable: true
last_delivery_status:
type: string
example: null
nullable: true
created_at:
type: string
example: '2026-05-13T18:00:42.000Z'
updated_at:
type: string
example: '2026-05-13T18:00:42.000Z'
secret:
type: string
example: 35ada3b45aa00df901b74b2b483114315a0c21f7c096f17e0bb29ab7d907fe32
_warning:
type: string
example: 'Save this secret now — it will NOT be shown again.'
422:
description: 'SSRF blocked URL'
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/validation'
status: 422
code: validation
title_fa: 'اعتبارسنجی ورودی شکست خورد'
title_en: 'Request validation failed'
errors:
url:
- 'آدرسهای داخلی شبکه (localhost / *.local / *.internal) مجاز نیستند.'
request_id: 01KRH8JRC4Y855P10CYC1C0AYS
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/validation'
status:
type: integer
example: 422
code:
type: string
example: validation
title_fa:
type: string
example: 'اعتبارسنجی ورودی شکست خورد'
title_en:
type: string
example: 'Request validation failed'
errors:
type: object
properties:
url:
type: array
example:
- 'آدرسهای داخلی شبکه (localhost / *.local / *.internal) مجاز نیستند.'
items:
type: string
request_id:
type: string
example: 01KRH8JRC4Y855P10CYC1C0AYS
tags:
- Webhooks
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
description: 'HTTPS endpoint to receive deliveries. Max 500 chars.'
example: 'https://example.com/webhooks/emza'
events:
type: array
description: 'Event names from the allowed list. Use `["*"]` for all.'
example:
- contract.completed
- contract.signer.signed
items:
type: string
active:
type: boolean
description: 'When false, subscription is paused (no deliveries). Default true.'
example: false
required:
- url
- events
'/api/v1/webhooks/{id}':
get:
summary: 'Get webhook subscription'
operationId: getWebhookSubscription
description: ''
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WS5N5GQWJDB3P8R26MG
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WS5N5GQWJDB3P8R26MG
tags:
- Webhooks
patch:
summary: 'Update webhook subscription'
operationId: updateWebhookSubscription
description: "Partial update — any field omitted is left unchanged. New `url` values\npass through the same SSRF guard as `POST /webhooks` (see Create endpoint).\n\nWhen `rotate_secret=true`, a fresh HMAC signing secret is generated and\nreturned in the response — the previous secret is invalidated immediately,\nso any in-flight or already-sent webhook deliveries signed with the old\nsecret will fail verification on the receiver side. Plan rotations during\na quiet period or pause via `active=false` first.\n\nScope: `webhooks:manage`. Idempotency-Key required (24h replay window)."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
id: 01krh7z0e8a4n39gf2ary73xbj
url: 'https://example.com/webhooks/emza'
events:
- '*'
active: false
failed_deliveries_count: 2
last_delivery_at: '2026-05-13T17:30:00.000Z'
last_delivery_status: delivered
properties:
data:
type: object
properties:
id:
type: string
example: 01krh7z0e8a4n39gf2ary73xbj
url:
type: string
example: 'https://example.com/webhooks/emza'
events:
type: array
example:
- '*'
items:
type: string
active:
type: boolean
example: false
failed_deliveries_count:
type: integer
example: 2
last_delivery_at:
type: string
example: '2026-05-13T17:30:00.000Z'
last_delivery_status:
type: string
example: delivered
tags:
- Webhooks
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
url:
type: string
description: 'New endpoint URL (subject to same SSRF guard as create).'
example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html'
events:
type: array
description: 'Replaces the entire events list. Same allowed values as create.'
example:
- architecto
items:
type: string
active:
type: boolean
description: 'Toggle the subscription on/off without deleting it.'
example: false
rotate_secret:
type: boolean
description: 'When true, issues a fresh secret (returned in response under `secret` + `_warning`).'
example: false
delete:
summary: 'Delete webhook subscription'
operationId: deleteWebhookSubscription
description: "Soft-deletes the subscription. In-flight deliveries that have already\nbeen queued will complete; no new deliveries are dispatched."
parameters: []
responses: { }
tags:
- Webhooks
parameters:
-
in: path
name: id
description: 'The ID of the webhook.'
example: B8c6F7NNfcj9m6RJYC6RtkmZN6
required: true
schema:
type: string
/api/v1/webhook-deliveries:
get:
summary: 'List webhook deliveries'
operationId: listWebhookDeliveries
description: ''
parameters:
-
in: query
name: subscription_id
description: 'Filter by subscription.'
example: architecto
required: false
schema:
type: string
description: 'Filter by subscription.'
example: architecto
-
in: query
name: event_name
description: 'Filter by event name.'
example: architecto
required: false
schema:
type: string
description: 'Filter by event name.'
example: architecto
-
in: query
name: status
description: pending|delivered|failed|dead-letter
example: architecto
required: false
schema:
type: string
description: pending|delivered|failed|dead-letter
example: architecto
-
in: query
name: page
description: ''
example: 16
required: false
schema:
type: integer
description: ''
example: 16
-
in: query
name: per_page
description: 'Max 100.'
example: 16
required: false
schema:
type: integer
description: 'Max 100.'
example: 16
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WSNFZ5ABRQPF7K8N2G1
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WSNFZ5ABRQPF7K8N2G1
tags:
- Webhooks
'/api/v1/webhook-deliveries/{id}':
get:
summary: 'Get webhook delivery'
operationId: getWebhookDelivery
description: 'Shows full payload, response code, error message, attempt history.'
parameters: []
responses:
401:
description: ''
content:
application/json:
schema:
type: object
example:
type: 'https://docs.elemza.com/errors/token_invalid'
status: 401
code: token_invalid
title_fa: 'توکن نامعتبر است'
title_en: 'Invalid token'
request_id: 01KRHE8WSWEFY6JFDHPGFTAHW3
properties:
type:
type: string
example: 'https://docs.elemza.com/errors/token_invalid'
status:
type: integer
example: 401
code:
type: string
example: token_invalid
title_fa:
type: string
example: 'توکن نامعتبر است'
title_en:
type: string
example: 'Invalid token'
request_id:
type: string
example: 01KRHE8WSWEFY6JFDHPGFTAHW3
tags:
- Webhooks
parameters:
-
in: path
name: id
description: 'The ID of the webhook delivery.'
example: B8c6F7NNfcj9m6RJYC6RtkmZN6
required: true
schema:
type: string
'/api/v1/webhooks/{id}/test':
post:
summary: 'Send a synthetic test event'
operationId: sendASyntheticTestEvent
description: "Fires a `webhook.test` event to verify the endpoint is reachable + signed\ncorrectly. Counts toward `webhook_deliveries` like a real event."
parameters: []
responses: { }
tags:
- Webhooks
parameters:
-
in: path
name: id
description: 'The ID of the webhook.'
example: B8c6F7NNfcj9m6RJYC6RtkmZN6
required: true
schema:
type: string
'/api/v1/webhook-deliveries/{id}/replay':
post:
summary: 'Replay webhook delivery'
operationId: replayWebhookDelivery
description: "Creates a NEW delivery row with the same payload + subscription. The\noriginal row keeps its terminal status (failed / dead-letter / delivered)."
parameters: []
responses: { }
tags:
- Webhooks
parameters:
-
in: path
name: id
description: 'The ID of the webhook delivery.'
example: B8c6F7NNfcj9m6RJYC6RtkmZN6
required: true
schema:
type: string