Skip to main content

Tracking and Recovering Bulk Sends

A bulk send delivers one agreement to many recipients at once. Propper creates a parent agreement and, from it, one copy per recipient group. Each copy is its own agreement that its recipients sign independently.

The Propper Sign API lets you follow a bulk send to completion: list its copies with their delivery and signing status, export them as CSV, download the signed documents as a ZIP, resend to recipients who have not yet acted, and cancel copies that have not gone out.

Before you start​

  • Scopes: sign:read to read copies, export them, and download the signed documents, sign:send to resend or cancel.
  • Starting a bulk send: for now, create and send bulk sends in the Propper web app. Upload your recipient list there and send. The API cannot yet add recipient groups to a bulk agreement. Once the send is underway, everything below works over the API.

Lifecycle at a glance​

StepEndpointScope
Check progressGET /v1/sign/agreements/{id}/bulk/copiessign:read
Export copies to CSVGET /v1/sign/agreements/{id}/bulk/copies/exportsign:read
Download signed documentsGET /v1/sign/agreements/{id}/bulk/downloadsign:read
Remind outstanding signersPOST /v1/sign/agreements/{id}/bulk/resendsign:send
Stop copies not yet sentPOST /v1/sign/agreements/{id}/bulk/cancelsign:send

1. Get the bulk agreement's id​

Every endpoint below takes the id of the bulk send's parent agreement. Copy it from the Propper web app: open the bulk send, and the id is the part of the page address after /sign/agreements/. If you pass the id of a copy or of an ordinary agreement, the bulk endpoints return 400 (resend) or an empty result.

2. Check progress​

GET /bulk/copies returns one page of copies plus a summary of the whole batch. The summary is the quickest way to tell whether a send is finished.

curl "https://api.propper.ai/v1/sign/agreements/$AGREEMENT_ID/bulk/copies?page=1&limit=25" \
-H "Authorization: Bearer $ACCESS_TOKEN"
{
"data": [
{
"copyId": "5d2b7f0e-8a51-4c43-9d1e-2f7a0c6b9e11",
"copyNumber": 1,
"status": "SENT",
"attempts": 1,
"lastAttemptAt": "2026-10-20T17:00:00.000Z",
"agreementId": "a3c1e9d4-0b7f-4f2a-8e65-91d0c4b7a2f3",
"agreementStatus": "IN_PROGRESS",
"recipients": [
{
"id": "c8e4f1a2-6d3b-4e9c-b0a7-5f2d8e1c3b49",
"name": "Ana Lopez",
"email": "ana.lopez@propper.ai",
"role": "Signer",
"status": "SIGNED",
"deliveryStatus": "delivered"
}
]
}
],
"pagination": { "total": 42, "page": 1, "limit": 25, "hasMore": true },
"summary": {
"copiesByStatus": { "PENDING": 0, "QUEUED": 0, "SENT": 40, "FAILED": 2, "CANCELLED": 0 },
"copiesByRecipientStatus": { "SENT": 10, "VIEWED": 5, "SIGNED": 25, "DECLINED": 0 },
"completedCopies": 25,
"failedCopies": 3,
"roles": [{ "role": "Signer", "order": 1, "total": 42, "byStatus": { "SIGNED": 25 } }],
"fixedRecipients": []
}
}

Copy status​

statusMeaning
PENDINGWaiting to be prepared.
QUEUEDThe copy's agreement exists and is waiting to go out, including copies queued for a reminder during a resend.
SENTDelivered to its recipients. Signing progress is on each recipient.
FAILEDCould not be sent. Check each recipient's deliveryStatus.
CANCELLEDStopped before it was sent.

In the summary, failedCopies also counts copies that were sent but whose waiting recipient's email bounced, so it can be higher than copiesByStatus.FAILED. To list those copies, filter with status=FAILED.

Filtering and sorting​

ParameterValues
page, limitPage number (from 1, default 1) and page size (1–100, default 20).
statusComma-separated copy statuses, e.g. FAILED,CANCELLED.
recipientStatusOne of SENT, VIEWED, SIGNED, DECLINED.
roleA recipient role name, e.g. Signer.
searchMatches recipient name or email.
sortBycopyNumber (default), status, or lastAttemptAt.
sortOrderasc (default) or desc.

An unknown or malformed value returns 400 rather than being ignored.

fixedRecipients lists recipients who are on every copy, such as an internal countersigner. signEachCopy tells you whether they sign each copy individually.

3. Export copies to CSV​

GET /bulk/copies/export takes the same filters (but no page or limit) and returns text/csv with one row per recipient. Use it to fix bad addresses offline when many copies failed:

curl "https://api.propper.ai/v1/sign/agreements/$AGREEMENT_ID/bulk/copies/export?status=FAILED" \
-H "Authorization: Bearer $ACCESS_TOKEN" -o failed-copies.csv

Columns, in order: copy, itemStatus, role, recipientName, recipientEmail, recipientStatus, deliveryStatus, attempts, lastAttemptAt, lastError, displayStatus. The lastError column is always empty in API exports; use deliveryStatus and displayStatus instead. In this file, a queued copy's itemStatus is written as EXPANDED. An export holds at most 10,000 rows, so narrow it with filters on very large sends.

Download the signed documents​

GET /bulk/download returns an application/zip archive with one PDF for every copy that has completed signing. Copies that are still in progress, failed, or cancelled are not included, so the archive grows as more copies complete. The filename is <agreement name>-bulk-documents.zip.

curl "https://api.propper.ai/v1/sign/agreements/$AGREEMENT_ID/bulk/download" \
-H "Authorization: Bearer $ACCESS_TOKEN" -o bulk-documents.zip
StatusMeaning
200The ZIP archive.
404No bulk send with this id exists in your organization.
409No copy has completed yet (error.code is NO_COMPLETED_COPIES). Try again once a copy is signed.

4. Remind outstanding signers​

POST /bulk/resend re-sends to every recipient who has not yet acted on a sent copy:

curl -X POST "https://api.propper.ai/v1/sign/agreements/$AGREEMENT_ID/bulk/resend" \
-H "Authorization: Bearer $ACCESS_TOKEN"
{
"agreementId": "9f6e2d1c-4b8a-4c7e-a3f5-0d2b6e8c1a74",
"resendCount": 15,
"startedAt": "2026-10-20T18:00:00.000Z",
"estimatedCompletionAt": "2026-10-20T18:05:00.000Z"
}

It returns 202 Accepted when reminders are queued and 200 OK with resendCount: 0 when nobody is outstanding. Reminders go out gradually; estimatedCompletionAt says when the last should be sent.

StatusWhen
400The agreement is not a bulk send, or it is already completed, declined, voided, or expired.
404No bulk agreement with that id in your organization.
409Another send or resend for this bulk agreement is still running. Wait, then retry.
429A resend ran recently. Wait the number of seconds in the Retry-After header.

5. Stop copies not yet sent​

POST /bulk/cancel cancels every copy still waiting to go out (PENDING or QUEUED), and stops the send or resend that is running. Copies that are SENT, FAILED, or CANCELLED are left alone; SENT recipients already have the email, so to withdraw one of those copies, void its own agreement (agreementId on the copy).

Cancelling is best-effort: an email that was already being delivered when you cancelled may still arrive. A copy cancelled while it was waiting for a reminder keeps its agreement open for signing; only the reminder is skipped.

curl -X POST "https://api.propper.ai/v1/sign/agreements/$AGREEMENT_ID/bulk/cancel" \
-H "Authorization: Bearer $ACCESS_TOKEN"
{ "cancelledCount": 12, "alreadyTerminalCount": 30, "sendStopped": true }

alreadyTerminalCount is the number of copies that were already SENT, FAILED, or CANCELLED. sendStopped is true when this call stopped a send or resend that was still running. Calling cancel again is safe and returns cancelledCount: 0.

Errors​

Every failure uses the standard error envelope described in Error Handling.