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