Guides
Documents
Create documents from PDFs, add recipients and fields, send them for signature, track status and download signed PDFs.
A document is a PDF sent for electronic signature — what the website calls an agreement. You upload a PDF, say who signs it and where, send it, and download the signed copy with its certificate when everyone has signed.
Lifecycle#
| Status | Meaning |
|---|---|
document.uploaded | Returned by the create call while the upload is stored. Reading the document back gives document.draft. |
document.draft | Processed and ready to send. |
document.sent | Sent; waiting for recipients. |
document.viewed | At least one recipient has opened the document. |
document.completed | Everyone has signed. The signed PDF and certificate can be downloaded. |
document.declined | A recipient declined to sign. |
document.voided | The sender cancelled the document. |
document.expired | The signing deadline passed before everyone signed. |
document.error | Processing failed after the upload was accepted (rare: unreadable files are refused at creation with 415 or 422). |
Changes of status trigger the document_state_changed webhook.
Create a document#
Send multipart/form-data with two parts: file (the PDF) and data (a JSON string). Everything is checked straight away — an unreadable file answers 415 or 422, an unknown text-tag role or unassigned form field 400. The answer is 201 with status document.uploaded; read it back and it is a document.draft, ready to send.
curl https://macaaw.com/public/v1/documents \
-H "Authorization: API-Key $MACAAW_API_KEY" \
-H "Idempotency-Key: 0b6c2c2e-7f1d-4a43-9d1b-3f1e8c3f4a10" \
-F "file=@agreement.pdf;type=application/pdf" \
-F 'data={"name":"Service agreement","recipients":[{"email":"client@example.com","first_name":"Ana","last_name":"Silva","role":"Client","signing_order":1},{"email":"legal@example.com","first_name":"Joe","last_name":"Park","role":"Legal","type":"cc"}],"fields":[{"type":"signature","role":"Client","page":1,"x":72,"y":640,"width":180,"height":48}],"parse_text_tags":true,"subject":"Please sign the service agreement","message":"Thanks for choosing us.","expires_in_days":14,"reminders":true,"tags":["sales"],"metadata":{"crm_deal_id":"4815"}}'The data object#
| Field | Type | Description |
|---|---|---|
name | string | Shown to recipients and in your lists. Required. |
recipients | array | At least one. Each: email, first_name, last_name, role (unique within the document), optional signing_order, locale (language of emails and the signing page) and type: signer (default) or cc (gets a copy, does not sign). Give any recipient a signing_order (1, 2…) and they sign in that order; without it everyone signs at the same time. |
fields | array | Fields placed by coordinates: type (signature, initials, name, date, text, checkbox), role, page (1-based), x, y, width, height in PDF points with the origin at the top-left of the page, optional required (default true for signatures, false otherwise) and name. |
parse_text_tags | boolean | Turn text tags such as {signature:Client} into fields. Default true. |
parse_form_fields | boolean | Turn the PDF's own form fields into fields, assigned by a name prefix Role. (for example Client.signature). Unassigned fields are listed in a 400. |
subject | string | Email subject (can also be given when sending). |
message | string | Message shown in the email and on the signing page. |
expires_in_days | 7 | 14 | 30 | Signing deadline. |
reminders | boolean | Send automatic reminders to recipients who have not signed. |
access_code | string | A code recipients must enter before they can see the document (not needed for embedded signing sessions). |
tags | string[] | Your own labels; filter lists with ?tag=. |
metadata | object | String keys and values for your own references. Returned in details and webhooks. |
fields, text tags or form fields. A tag for a role that has no recipient is refused with 400.{
"id": "58b66391-30fa-4219-a9b3-4f7badfad583",
"uuid": "58b66391-30fa-4219-a9b3-4f7badfad583",
"name": "Service agreement",
"status": "document.uploaded",
"date_created": "2026-10-30T11:35:00.342000Z",
"date_modified": "2026-10-30T11:35:00.342000Z",
"expiration_date": null,
"sandbox": false
}Check the status#
{
"id": "58b66391-30fa-4219-a9b3-4f7badfad583",
"name": "Service agreement",
"status": "document.sent",
"date_created": "2026-10-30T11:35:00.342000Z",
"date_modified": "2026-10-30T11:36:12.883000Z",
"expiration_date": "2026-11-13T11:36:12.883000Z",
"sandbox": false
}GET /documents/{id}/details adds recipients (each with status: waiting (not their turn yet), sent, viewed, completed or declined, plus has_completed, viewed_at, completed_at), fields, tags, metadata, the event timeline, the sender and download_available.
List documents#
| Query | Description |
|---|---|
q | Search by name. |
status | One status, e.g. document.completed. |
tag | Only documents with this tag. |
created_from | created_to — ISO 8601 date range. |
order_by | date_created or -date_created (newest first, the default). |
page | count — page number from 1, and page size up to 100 (default 50). |
Send#
Only a document.draft can be sent (anything else is 409). Body: optional subject, message and silent. With "silent": true no emails go out — use this with embedded signing. Sending a live document counts one agreement against your monthly limit.
Remind, void and delete#
| Call | What it does |
|---|---|
POST /documents/{id}/remind | Emails a reminder to one recipient ({ "recipient_id": "…" }) or to everyone who has not signed yet. Answers { "reminded": [ids], "skipped": [{ "recipient_id", "reason" }] }; on a silent document it is 409. |
PATCH /documents/{id}/status | { "status": "document.voided", "note": "…" } cancels a document.sent document; recipients are told. |
DELETE /documents/{id} | Deletes the document and its files now. Answers 204. |
Download#
For a completed document this returns the signed PDF with the signing certificate appended; before that it returns the original PDF. Sandbox downloads are stamped on every page.