Developer

API Docs

Complete reference for the Mocha Signature external API — every endpoint, request field, response body, webhook payload and status code.

Overview

Base URLs, authentication and the endpoint index.

All endpoints are POST with a JSON body — including the read-only ones — and every request must carry both the X-Tenant and Api-Key headers. Operations are scoped to your own tenant account.

EnvironmentBase URL
UAThttps://services.us.uat.mochatechnologies.com/signature/api/V1
Productionhttps://services.ap.mochatechnologies.com/signature/api/V1

Endpoints

GroupEndpointPurpose
Templates/get-template-by-userList the templates available to your account.
Templates/get-template-detailRead a template's recipient roles and privileges.
Envelopes/send-envelopCreate an envelope from a template and email it out for signing.
Envelopes/envelop-trackingTrack an envelope and each recipient's state.
Webhooks/webhook/deliveriesInspect the delivery log for a single webhook attempt.
Additional/get-signing-linksPer-recipient signing URLs for an envelope.
Additional/get-template-by-doc-codeList templates keyed on document_id instead of user.
Additional/insert-template-fieldCopy a template's field definitions onto an existing envelope.

Additional endpoints are not part of the published guide

The three endpoints tagged Additional exist on the external surface but are not part of the supported integration guide. They are functional but may change without notice — prefer the documented Templates, Envelopes and Webhooks endpoints.

Response shapes vary slightly

Some endpoints return status as a boolean plus a separate status_code, others return status as the numeric HTTP code. The HTTP status code is always authoritative; read data for the payload.

Get Templates By User ID

Templates — list everything available to the account.

GET/get-template-by-userX-Tenant + Api-Key required

Fetches all templates associated with the user account. Each item includes pdf_preview, a pre-signed S3 URL to the template PDF that is valid for 10 minutes — fetch it promptly or re-request the list. pdf_preview is null when the underlying object is missing from storage.

Query parameters

FieldTypeRequiredDescription
user_idstringRequiredThe account's user identifier.
template_typestring | nullOptionalOptional filter on the template's type, e.g. contract.
Request
GET /get-template-by-user?user_id=acf28c4584f007dca67b

Response — 200

Returns the template list, which may be empty. See TemplateListItem in the Schema Reference.

200 OK
{
  "status_code": 200,
  "status": true,
  "message": "Template record exist.",
  "data": [
    {
      "template_id": "73766101002022",
      "user_id": "acf28c4584f007dca67b",
      "document_id": "1002022",
      "title": "Non-Disclosure Agreement",
      "description": "Standard mutual NDA",
      "total_pages": 3,
      "user_name": "Jane",
      "email": "jane@example.com",
      "job": "Legal Counsel",
      "industry": "Technology",
      "pdf_path": "acf28c4584f007dca67b/documents/73766101002022/73766101002022.pdf",
      "pdf_preview": "https://s3.amazonaws.com/...?X-Amz-Expires=600&X-Amz-Signature=..."
    }
  ]
}

Status codes

StatusMeaning
200 OKThe template list is returned. The list may be empty.
401 UnauthorizedThe supplied Api-Key is invalid or has been revoked. Also returned when X-Tenant is absent, as API keys are resolved within their tenant.
403 ForbiddenThe Api-Key header was not supplied.
503 Service UnavailableX-Tenant could not be resolved to a known tenant, or an authentication dependency was unavailable. The request was not processed and may be retried.
422 Unprocessable ContentThe request failed validation, e.g. user_id was omitted. msg maps each rejected field to its messages.
500 Internal Server ErrorAn unexpected error occurred while building the template list.

Get Template Details

Templates — read the recipient contract before sending.

GET/get-template-detailX-Tenant + Api-Key required

Retrieves a specific template's configuration, including the recipient roles it expects. recipients_role and required_recipient define the contract for Send Envelope: you must supply exactly these roles, and exactly that many recipients.

Query parameters

FieldTypeRequiredDescription
template_idstringRequiredIdentifier shown in the Templates list in the web app.
Request
GET /get-template-detail?template_id=73766101002022

Response — 200

200 OK
{
  "status": 200,
  "message": "Template record exist.",
  "data": {
    "template_id": "73766101002022",
    "title": "Non-Disclosure Agreement",
    "discription": "Standard mutual NDA",
    "status": 1,
    "required_recipient": 2,
    "recipients_role": ["Signer 1", "Approver"]
  }
}

204 carries no body

When no template maps to the supplied template_id the API replies 204. Per HTTP semantics the body of a 204 is not transmitted — branch on the status code, not on the body.

Status codes

StatusMeaning
200 OKThe template was found and is returned.
204 No ContentThe request succeeded, but no template matches the supplied template_id. No response body is returned.
401 UnauthorizedThe supplied Api-Key is invalid or has been revoked. Also returned when X-Tenant is absent, as API keys are resolved within their tenant.
403 ForbiddenThe Api-Key header was not supplied.
503 Service UnavailableX-Tenant could not be resolved to a known tenant, or an authentication dependency was unavailable. The request was not processed and may be retried.
422 Unprocessable ContentThe request failed validation, e.g. template_id was omitted. msg maps each rejected field to its messages.
500 Internal Server ErrorAn unexpected error occurred while reading the template.

Send Envelope

Envelopes — create an envelope from a template and email it out.

POST/send-envelopX-Tenant + Api-Key required

Creates an envelope from a template and emails it to the recipients for signing. Recipients who do not yet exist as users are provisioned automatically and added to the sender's contacts.

Recipient roles must match the template exactly

Call Get Template Details first: the number of entries in recipients_role must equal required_recipient, and every role value must appear in the template's recipients_role list. For a sequence template the entries must also be supplied in the template's own signing order — role 1 first. A mismatch fails the request with a 422.

Request body

FieldTypeRequiredDescription
user_idstringRequiredSending account's user identifier.
company_namestringRequiredSender's company name; also used when auto-provisioning new recipients.
email_subjectstringRequiredSubject line of the signing invitation email.
messagestringRequiredBody of the invitation email. Declared optional in validation but enforced downstream — omitting it returns 422, so always send it.
template_idstringRequiredTemplate to build the envelope from.
recipients_roleRecipientRole[]RequiredOne entry per template recipient role, each with role, name and email.
metadataobject | nullOptionalFree-form object stored with the envelope and echoed back verbatim in every webhook payload. Use it to carry your own correlation ids.
file_pathstring | nullOptionalOptional override for the document to send. Accepts a base64-encoded PDF (raw, or a data:application/pdf;base64,… URI). Omit it — the normal case — to use the template's own PDF.
statusstring | nullOptionalReserved. Accepted for backwards compatibility and currently has no effect.

RecipientRole

FieldTypeRequiredDescription
rolestringRequiredMust match one of the template's recipient role titles exactly.
namestringRequiredRecipient's name. Used as the first name when provisioning a new user.
emailstring (email)RequiredRecipient's email. If no account exists for it, a user and a sender-side contact are created automatically.
Request
{
  "user_id": "acf28c4584f007dca67b",
  "company_name": "Mocha Technologies",
  "email_subject": "Please sign the Non-Disclosure Agreement",
  "message": "Kindly review and sign the attached document.",
  "template_id": "73766101002022",
  "recipients_role": [
    {
      "role": "Signer 1",
      "name": "John Doe",
      "email": "john.doe@example.com"
    },
    {
      "role": "Approver",
      "name": "Alice Smith",
      "email": "alice.smith@example.com"
    }
  ],
  "metadata": {
    "order_id": "ORD-10021",
    "source": "crm"
  }
}

Response — 200

200 OK
{
  "status": true,
  "message": "Send envelope successfully!",
  "envelope_id": "b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc",
  "sending_mode": "sequence",
  "status_code": 200
}

Persist envelope_id

It is the key for Envelope Tracking and appears in every webhook payload for this envelope.

Response — 404

Returned when user_id or template_id does not match any existing record.

404 — template not found
{
  "status": false,
  "message": "Template [73766101002022] does not exist",
  "status_code": 404
}

Response — 422

Returned when recipients_role does not map onto the template's own roles (a different count, an unknown role name, or — for a sequence template — the wrong order), or when a recipient without an existing account could not be provisioned — for example an email address the identity service considers undeliverable. In both cases message carries the specific reason.

422 — role mismatch
{
  "status": false,
  "message": "Recipients role can not map with required template roles.",
  "status_code": 422
}

Status codes

StatusMeaning
200Envelope created and emailed to the recipients.
401Api-Key missing, revoked, or not owned by X-Tenant.
404Tenant not found, or user_id/template_id does not match any existing record.
422Validation failed, a required value was missing (user_id, template_id, recipients_role, email_subject and message are all enforced at this stage), recipients_role does not map onto the template's roles (wrong count, unknown role, or wrong order for a sequence template), or a new recipient could not be provisioned (e.g. an undeliverable email address).
500An unexpected error occurred while processing the request.
503Authentication dependency unreachable. Safe to retry.

Envelope Tracking

Envelopes — current state of the envelope and each recipient.

GET/envelop-trackingX-Tenant + Api-Key required

Tracks an envelope and each recipient's state. Use it to render progress in your own UI, and as the reconciliation fallback if you miss a webhook.

Mind the spelling

The query parameter is envelop_id (single e), while Send Envelope returns it as envelope_id. Map the value across that difference.

Query parameters

FieldTypeRequiredDescription
envelop_idstringRequiredThe envelope_id returned by Send Envelope.
Request
GET /envelop-tracking?envelop_id=23b89b780dd1ff7b6885a684483a37ef

Response — 200

200 OK
{
  "status": 200,
  "message": "Envelope tracking fetched successfully.",
  "data": {
    "envelope_id": "23b89b780dd1ff7b6885a684483a37ef",
    "mail_status": "Email Sent",
    "envelope_completion_status": "Pending",
    "recipients": [
      {
        "recipient_name": "John Doe",
        "recipient_email": "john.doe@example.com",
        "status": "Completed",
        "decline_reason": null,
        "viewed_at": "2026-08-03 09:41:02",
        "completed_at": "2026-08-03 09:44:37"
      },
      {
        "recipient_name": "Alice Smith",
        "recipient_email": "alice.smith@example.com",
        "status": "Pending Signature",
        "decline_reason": null,
        "viewed_at": null,
        "completed_at": null
      }
    ]
  }
}

Status values

FieldPossible values
mail_statusEmail Sent, Email Failed
envelope_completion_statusPending, Completed, Rejected (a recipient declined), N/A
recipients[].statusPending Signature, Completed, Declined, N/A

Status codes

StatusMeaning
200Tracking data for the envelope.
401Api-Key missing, revoked, or not owned by X-Tenant.
404No envelope matches the supplied envelop_id.
422The request failed validation, e.g. envelop_id was omitted.
500Unexpected failure.
503Authentication dependency unreachable.

Webhook Events

What Mocha Signature POSTs to your endpoint.

Configure your endpoint in the web app under Settings → Custom Webhook. Two events are delivered, both signed with HMAC-SHA256 over the raw request body. Full setup and verification instructions are in Getting Started → Webhooks.

document.completed

Delivered once every required recipient has completed the envelope. data.document.download_url is a pre-signed S3 link to the finished PDF, valid for 15 minutes — download and store the file on receipt rather than persisting the URL.

Delivery
POST https://your-endpoint.example.com/hooks/mocha-signature

X-Webhook-Event: document.completed
X-Webhook-Delivery-Id: bd142d9e-31f6-459f-b8d8-5c5cb902fbd9
X-Webhook-Timestamp: 2026-08-03T10:24:11.482000Z
X-Authorization-Digest: HMACSHA256
X-Webhook-Signature-1: <base64 HMAC-SHA256 of the raw body, secret #1>

{
  "event": "document.completed",
  "data": {
    "user_id": "acf28c4584f007dca67b",
    "envelop_id": "b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc",
    "status": "completed",
    "generated_at": "2026-08-03T10:24:11.000000Z",
    "metadata": {
      "order_id": "ORD-10021",
      "source": "crm"
    },
    "document": {
      "file_name": "b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc.pdf",
      "download_url": "https://s3.amazonaws.com/...?X-Amz-Expires=900&X-Amz-Signature=...",
      "mime_type": "application/pdf"
    }
  },
  "timestamp": "2026-08-03T10:24:11.482000Z"
}

document.declined

Delivered when a recipient declines or rejects the envelope. The signing flow stops at that point; remaining recipients are not asked to sign. Use Envelope Tracking to read which recipient declined and their decline_reason.

Payload
{
  "event": "document.declined",
  "data": {
    "user_id": "acf28c4584f007dca67b",
    "envelop_id": "b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc",
    "status": "declined",
    "generated_at": "2026-08-03T11:02:56.000000Z",
    "metadata": {
      "order_id": "ORD-10021"
    },
    "document": {
      "file_name": "b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc.pdf",
      "download_url": "https://s3.amazonaws.com/...?X-Amz-Expires=900&X-Amz-Signature=...",
      "mime_type": "application/pdf"
    }
  },
  "timestamp": "2026-08-03T11:02:56.913000Z"
}

Headers

HeaderRequiredDescription
X-Webhook-EventYesdocument.completed or document.declined.
X-Webhook-Delivery-IdYesUnique id for this delivery attempt — a retry carries a different one. Store it: it is the lookup key for Webhook Deliveries. Do not use it as an idempotency key.
X-Webhook-TimestampYesISO-8601 time the delivery was sent.
X-Authorization-DigestYesSignature algorithm — always HMACSHA256.
X-Webhook-Signature-1YesBase64 HMAC-SHA256 of the raw request body, computed with your first active secret.
X-Webhook-Signature-2NoPresent when a second secret key is active. Accept the request if any supplied signature matches.

Acknowledging

  • Return any 2xx to acknowledge. Non-2xx — or a timeout past 15 seconds — marks the delivery failed and schedules a retry.
  • Delivery is retried up to 4 attempts — immediately, then after 60s, 5m and 15m. Each attempt makes up to 3 HTTP requests, so a fully failing delivery can arrive up to 12 times over roughly 21 minutes.
  • Treat handlers as idempotent, keyed on data.envelop_id plus event — never on X-Webhook-Delivery-Id, which changes on every attempt.

Webhook Deliveries

Webhooks — the delivery log for a single attempt.

POST/webhook/deliveriesX-Tenant + Api-Key required

Returns the delivery log for a single webhook attempt — what was sent, the response your endpoint returned, whether it succeeded, and how long it took. Each retry is a separate record with its own delivery id, so this describes one attempt rather than the whole series. Look it up by the X-Webhook-Delivery-Id header value received on the webhook call, so store that header when handling webhooks. Only deliveries belonging to your own tenant are visible.

Re-verifying an old delivery

request_payload is the exact body that was signed and sent, not a re-serialisation — so you can re-check the HMAC for a past delivery from nothing but its id. One catch: this response is JSON-encoded with slashes escaped, so request_payload arrives as a string containing https:\/\/…. JSON-decode that field first, then hash the decoded string — hashing the raw response text will not match.

Request body

FieldTypeRequiredDescription
delivery_idstring (max 100)RequiredValue of the X-Webhook-Delivery-Id header from the webhook call.
Request
{
  "delivery_id": "bd142d9e-31f6-459f-b8d8-5c5cb902fbd9"
}

Response — 200

200 OK
{
  "status": 200,
  "message": "Delivery fetched successfully",
  "data": {
    "envelop_id": "b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc",
    "id": 4821,
    "webhook_config_id": 17,
    "delivery_id": "bd142d9e-31f6-459f-b8d8-5c5cb902fbd9",
    "event_type": "document.completed",
    "request_payload": "{\"event\":\"document.completed\",\"data\":{...}}",
    "response_status": 200,
    "status": "success",
    "response_body": "{\"received\":true}",
    "error_message": null,
    "attempt_count": 1,
    "duration_ms": 214,
    "delivered_at": "2026-08-03 10:24:12",
    "created_at": "2026-08-03T10:24:11.000000Z",
    "updated_at": "2026-08-03T10:24:12.000000Z",
    "webhook_config": {
      "id": 17,
      "tenant_id": "acme",
      "name": "CRM completion hook",
      "url": "https://crm.example.com/hooks/mocha-signature"
    }
  }
}

request_payload is the exact signed string

Signatures are computed over that string verbatim. If your verification is failing, compare it byte-for-byte with what your handler hashed.

Status codes

StatusMeaning
200Delivery log entry.
401Api-Key missing, revoked, or not owned by X-Tenant.
404No delivery with that id exists for your tenant.
422Validation failed.
500Unexpected failure.
503Authentication dependency unreachable.

Get Templates By Document Code

Additional — the same listing keyed on document_id.

POST/get-template-by-doc-codeX-Tenant + Api-Key required

Not part of the published integration guide

This endpoint is functional but may change without notice.

Same listing as Get Templates By User ID, keyed on the template's document_id instead of the user. Returns raw template rows — no pdf_preview is generated.

Request body

FieldTypeRequiredDescription
document_codestringRequiredThe template's document_id.
Request
{
  "document_code": "1002022"
}

Status codes

StatusMeaning
200Matching templates.
401Api-Key missing, revoked, or not owned by X-Tenant.
422Validation failed.
500Unexpected failure.

Insert Envelope Template Fields

Additional — repair or extend an envelope assembled another way.

POST/insert-template-fieldX-Tenant + Api-Key required

Send Envelope already does this

Copying a template's field definitions onto an envelope is part of the Send Envelope flow. Call this only when repairing or extending an envelope that was assembled another way. Not part of the published integration guide.

Request body

FieldTypeRequiredDescription
template_idstringRequiredTemplate whose field definitions are copied.
envelop_idstringRequiredEnvelope the fields are inserted onto.
recipient_contact_idsinteger[]RequiredContact ids, in the template's recipient-role order.
Request
{
  "template_id": "73766101002022",
  "envelop_id": "b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc",
  "recipient_contact_ids": [3312, 3313]
}

Response — 201

Returned when at least one field was inserted.

201 Created
{
  "status": true,
  "message": "Envelope fields inserted successfully",
  "data": {
    "insertedRows": 12
  }
}

200 when nothing matched

If template_id and envelop_id both exist but none of the template's fields map onto the supplied recipient_contact_ids, the request still succeeds — with 200 instead of 201, since nothing was actually created, and insertedRows: 0.

Response — 404

Returned when template_id or envelop_id does not match any existing record.

404 — envelope not found
{
  "status": false,
  "message": "Envelope [b5db5fe9-8a44-4ecc-b3f4-222b14b1d5dc] does not exist"
}

Status codes

StatusMeaning
200Succeeded, but no fields matched — nothing was inserted.
201At least one field was inserted.
401Api-Key missing, revoked, or not owned by X-Tenant.
404template_id or envelop_id does not match any existing record.
422The request failed validation, e.g. template_id, envelop_id or recipient_contact_ids was omitted.
500An unexpected error occurred.

Schema Reference

The objects returned inside data.

TemplateListItem

FieldTypeDescription
template_idstringTemplate identifier — send this to Send Envelope.
user_idstringTemplate owner's user identifier.
document_idstringUnderlying document code.
titlestringTemplate title.
descriptionstring | nullTemplate description.
total_pagesinteger | nullPage count of the template PDF.
user_namestringTemplate owner's first name.
emailstring (email)Template owner's email.
jobstring | nullOwner's job title.
industrystring | nullOwner's industry.
pdf_pathstringStorage key of the template PDF.
pdf_previewstring | nullPre-signed URL to the template PDF, valid for 10 minutes. null when the object is not present in storage.

TemplateDetail

FieldTypeDescription
template_idstringTemplate identifier.
titlestringTemplate title.
discriptionstring | nullTemplate description. The misspelling is intentional — it is the wire field name.
statusintegerTemplate state flag.
required_recipientintegerNumber of recipients the template expects. Send Envelope requires exactly this many entries in recipients_role.
recipients_rolestring[]The recipient role titles defined on the template. Every role you send must be one of these.

EnvelopeTracking

FieldTypeDescription
envelope_idstringThe envelope being tracked.
mail_statusstringWhether the signing invitation was dispatched: Email Sent or Email Failed.
envelope_completion_statusstringOverall envelope state: Pending, Completed, Rejected or N/A. Rejected means a recipient declined.
recipientsTrackedRecipient[]Recipients in signing order.

TrackedRecipient

FieldTypeDescription
recipient_namestring | nullRecipient's name.
recipient_emailstring | nullRecipient's email.
statusstringPending Signature, Completed, Declined or N/A.
decline_reasonstring | nullThe recipient's stated reason. Non-null only when status is Declined.
viewed_atstring | nullWhen the recipient first opened the document. null if never opened.
completed_atstring | nullWhen the recipient finished their action.

WebhookEnvelope

FieldTypeDescription
eventstringdocument.completed or document.declined.
dataWebhookDocumentDataEvent payload.
timestampstring (date-time)ISO-8601 time the payload was built.

WebhookDocumentData

FieldTypeDescription
user_idstringOwner of the envelope — the sending account.
envelop_idstringMatches the envelope_id returned by Send Envelope.
statusstringcompleted or declined.
generated_atstring (date-time)When the event was generated.
metadataobject | nullThe metadata supplied on Send Envelope, returned verbatim.
document.file_namestring | nullFile name of the signed PDF.
document.download_urlstring | nullPre-signed URL to the signed PDF, valid for 15 minutes. Download the file on receipt; do not store the URL. null if the link could not be generated.
document.mime_typestringAlways application/pdf.

WebhookDelivery

FieldTypeDescription
delivery_idstring (uuid)The delivery you looked up.
envelop_idstring | nullExtracted from the delivered payload for convenience.
idintegerInternal delivery row id.
webhook_config_idintegerConfiguration this delivery belongs to.
event_typestringEvent that triggered the delivery.
request_payloadstringThe exact JSON string that was signed and sent. Signatures are computed over this.
response_statusinteger | nullHTTP status your endpoint returned. null if the request never completed.
statusstringpending, success or failed.
response_bodystring | nullBody your endpoint returned.
error_messagestring | nullTransport-level failure detail, e.g. connection timeout.
attempt_countintegerCounter for this record only — 1 on success, 2 once it has failed. It is not a running total across retries: each retry is logged as its own record with its own delivery_id, so this never exceeds 2.
duration_msinteger | nullRound-trip time of the attempt.
delivered_atstring | nullWhen the delivery succeeded.
webhook_configobjectid, tenant_id, name and url of the configuration.

Error Reference

Every status code the API returns, and the body that comes with it.

StatusMeaning
200Success. Read the payload from data.
201Created — Insert Envelope Template Fields only.
204No template maps to the supplied template_id. No body is transmitted.
400Insert failed — Insert Envelope Template Fields only.
401The Api-Key header is missing, revoked, or does not belong to the tenant in X-Tenant. Also returned if the platform could not issue an internal token for the key's user.
404The tenant in X-Tenant does not exist, the key's user has no email on record, or the requested envelope / delivery was not found.
422Request body failed validation. msg holds a field → messages map.
500Unexpected failure while processing the request. error carries the reason.
503A dependency needed to authenticate the request was unreachable. Safe to retry after a short backoff.

Error bodies

ShapeReturned byFields
ValidationErrorBody422status (false), msg — field name → list of messages.
ServerErrorBody500status (false), message, error, status_code.
MessageBody401, 404, 503message — e.g. Invalid API Key, Tenant 'acme' not found., Central service unavailable.

Retry policy

Retry 503 and transport failures with exponential backoff — the request was not processed. Never retry a 422; it will fail identically until the payload is fixed.