Status Updates
Push signature lifecycle events from your middleware back to Ironclad so the workflow can move forward.
Your middleware notifies Ironclad of signature lifecycle changes by calling the Ironclad public API. This is the inbound half of the integration. See BYOSP Overview for the full picture.
PATCH /public/api/v1/signature-requests/{id}
Update Custom Signature Request Status (BYOSP)
{id}is thesignatureRequestIdIronclad sent you at initiation.- Returns
204 No Contenton success. - Idempotent at the signature request level. Once a signature request has reached
completedorvoided, further pushes are no-ops.
Authentication and Scope
- Authorization:
Authorization: Bearer <token>, using an Ironclad public API OAuth 2.0 bearer token. No CSRF token is needed; the public API is header-authenticated. - Required OAuth scope:
public.signatureRequests.updateRequest.
The Token Must Belong to the Provider's Own Client AppIronclad looks up the custom signature provider that the signature request was sent through and compares its current client app to the
clientIdon your bearer token.A token minted for a different provider in the same company returns
403, as does any token without aclientId(i.e. a legacy company-scoped bearer token). Use the client app created on the provider in Ironclad Setup.
Two further access rules apply:
- A signature request that was not pinned to a specific signature provider cannot be bound to a caller and returns
403on every push. - The token's company must own the referenced signature request. A request for one owned by a different company returns
404rather than403, to avoid leaking existence.
User Context Is RequiredYour client app authenticates with the client credentials grant, so every push must also carry a user header (either
x-as-user-idorx-as-user-email) with a valid user ID or email. The request's scope and context are evaluated with respect to that user.See the client credentials user requirement in Authenticate a Request.
Use a Dedicated Integration User
We recommend creating a dedicated integration user in Ironclad for your middleware to act as, rather than sending a particular employee's identity on the x-as-user-email header.
- Stability. The integration keeps working when people change roles or leave the company. Pointing at an individual means their deactivation silently breaks every status push.
- Clear attribution. Signature activity that Ironclad records for these updates is traceable to the integration rather than to a person who did not perform the action.
- Scoped permissions. The user's permissions bound what the request can do, so a purpose-built user can be granted only what the middleware needs.
Use that user's email or ID consistently on every push.
Request Body
Unknown or extra keys are rejected.
Send the Full Signatures Array on Every PushEach push replaces the signer list on the signature request. Include an entry for every signer, including those whose state has not changed.
This is not a partial update. Do not send only the signer who just acted.
| Field | Type | Required | Description |
|---|---|---|---|
status | pending | completed | voided | Yes | The status of the signature request as a whole. Ironclad trusts this over any inference from signer states. |
signatures[] | array of objects | Yes | Every signer on the signature request, including those whose state has not changed. |
signatures[].roleName | string | Yes | Must match a roleName from the original initiation request, and must not repeat within a single push. |
signatures[].status | awaiting_signature | signed | declined | Yes | This signer's current state. |
signatures[].signer.name | string | Yes | The signer's current full name, as your provider has it. |
signatures[].signer.email | string (email) | Yes | The signer's current email address, as your provider has it. |
signatures[].declineReason | string | No | Persisted only when this signer's status is declined. |
signatures[].firstViewedAt | string (ISO-8601) | No | When the signer first viewed the request. Once Ironclad has a value for a signer it is never overwritten, so it is safe to send the same value on every push. |
Reassignment and DelegationAlways send the current
signer.nameandsigner.emailfrom your provider. Sending the current values is how a reassignment or delegation at your provider reaches Ironclad. There is no separate reassignment call.
nameon its own does not update the signer. Ironclad applies a changed name only when it arrives alongside a changed
Signer title and routing order are owned by Ironclad. They are retained from the values established at initiation and are neither sent nor accepted here.
A push after one of two signers has signed. Both signers are present:
{
"status": "pending",
"signatures": [
{
"roleName": "Buyer",
"status": "signed",
"signer": {
"name": "Kim Lam",
"email": "[email protected]"
},
"firstViewedAt": "2026-01-15T09:30:00Z"
},
{
"roleName": "Seller",
"status": "awaiting_signature",
"signer": {
"name": "Jimothy Kim",
"email": "[email protected]"
}
}
]
}A push where the second signer declined:
{
"status": "voided",
"signatures": [
{
"roleName": "Buyer",
"status": "signed",
"signer": {
"name": "Kim Lam",
"email": "[email protected]"
},
"firstViewedAt": "2026-01-15T09:30:00Z"
},
{
"roleName": "Seller",
"status": "declined",
"signer": {
"name": "Jimothy Kim",
"email": "[email protected]"
},
"firstViewedAt": "2026-01-15T11:02:00Z",
"declineReason": "Terms need revision before signing."
}
]
}Rules to be aware of:
- Every
signatures[].roleNamemust be one of the role names from the original initiation request, or the call returns400. - A
roleNamemust not repeat within a single push, or the call returns400. - Once Ironclad has a
firstViewedAtvalue for a signer, later pushes do not overwrite it. It is safe to send the same value on every push. declineReasonis stored only when that signer'sstatusisdeclined, and is ignored otherwise.
Completion Must Be Explicit
Ironclad does not infer that a signature request is finished from the state of its signers. The request-level status is the only thing that completes it.
A push where every signer is signed but status is still pending leaves the request open. The workflow stays on the Sign step, the signed document is not collected, and nothing downstream fires, even though signing is done from your provider's perspective.
SendcompletedWith the Final SignatureSet
statustocompletedon the same push that reports the last signer assigned, rather than sending the signature first and following up with a second push to complete the request.A single push leaves no window in which every signer has signed but the workflow has not advanced. It also removes the failure mode where the follow-up push never arrives (i.e. after a crash, a network fault, or a dropped retry), leaving the request stranded open with no signer left to act.
The same applies to termination. When your provider ends a request without full signature, report the terminal request status in the same push as the signer state, sending status: "voided" alongside the signer's status: "declined".
A declined signer closes the request on its own, so the workflow leaves the Sign step either way. What the request-level voided adds is marking the request itself terminal, which is what makes later pushes no-ops. Sending both together is safe, and the decline is still attributed to the signer who declined.
Ironclad does not distinguish one provider-side termination from another. A voided request with no declined signer is recorded without a specific cause, so an expiration and a cancellation on your provider's side are indistinguishable to Ironclad.
Once completed or voided is recorded, further pushes are no-ops, so there is no cost to including the final state as early as you can determine it.
Response Codes
| Code | Meaning |
|---|---|
204 | Success, including an idempotent no-op on a signature request that is already completed or voided. |
400 | Malformed body, unknown key, a roleName not present in the original request, a roleName that repeats within the push, or a malformed x-as-user-id value. |
401 | Missing or invalid bearer token. |
403 | Token lacks the public.signatureRequests.updateRequest scope, the caller is not the signature provider's current client app, or the acting user on x-as-user-id / x-as-user-email is missing, unrecognized, or not a member of the token's company (see Use a Dedicated Integration User). |
404 | Unknown signatureRequestId, or the signature request belongs to a different company. |
Example
curl -X PATCH \
"https://ironcladapp.com/public/api/v1/signature-requests/3f0c8b12-5e44-4c1a-9d77-2b91f0a4de21" \
-H "Authorization: Bearer $IRONCLAD_TOKEN" \
-H "x-as-user-email: $IRONCLAD_USER_EMAIL" \
-H "Content-Type: application/json" \
-d '{
"status": "completed",
"signatures": [
{
"roleName": "Buyer",
"status": "signed",
"signer": { "name": "Kim Lam", "email": "[email protected]" }
},
{
"roleName": "Seller",
"status": "signed",
"signer": { "name": "Jimothy Kim", "email": "[email protected]" }
}
]
}'Updated about 6 hours ago

