Middleware Endpoints

The five HTTPS endpoints your middleware must implement for Ironclad to initiate, track, and complete a signature request.

These endpoints are required in order to use the BYOSP feature. All paths are relative to the endpointUrl you configured in Ironclad Setup.

{id} is the Ironclad signature request ID, a UUID that Ironclad generates at initiation, sends as signatureRequestId in the initiate body, and passes as a path segment on every call after it.

📌

Every Request Is Signed

Every outbound request carries the X-Ironclad-Request-Signature header. We recommend verifying it before acting on the request. See Request Signing.

Endpoint Summary

OperationMethod and pathRequest bodyExpected response
InitiatePOST {endpointUrl}multipart/form-data200 JSON { status: "pending", signatures[] }
StatusGET {endpointUrl}/{id}/statusnone200 JSON { status, signatures[] }
CancelPOST {endpointUrl}/{id}/cancel{}2xx
RemindPOST {endpointUrl}/{id}/remind{ "roleName": "..." }2xx
DocumentGET {endpointUrl}/{id}/documentnone200 application/pdf (≤ 50 MB)

Non-2xx responses and timeouts are surfaced to Ironclad as middleware errors. Status, cancel, and remind must respond within 5 seconds; initiate and document fetch within 15 seconds.

Initiate

POST {endpointUrl}

Sent as multipart/form-data with these parts:

PartContent typeDescription
bodyapplication/jsonThe JSON payload described below. This is the part covered by the request signature.
document_0, document_1, …application/pdfOne part per PDF file, indexed in order.
📌

Single Document Today

Ironclad currently sends a single document per signature request. The indexed multipart parts exist for forward compatibility.

Request Body

FieldTypeRequiredDescription
signatureRequestIdstring (UUID)YesThe signature request ID. Send this as the {id} path parameter on inbound pushes.
routingTypesequential | parallelYesSigning order for the request.
signers[]array of objectsYesThe signers on the request.
signers[].roleNamestringYesUnique within the request. The only signer identifier on the wire.
signers[].namestringYesThe signer's full name.
signers[].emailstring (email)YesThe signer's email address.
signers[].titlestringYesThe signer's title. May be an empty string.
signers[].routingOrderintegerConditionalThe order in which this signer signs. Present when routingType is sequential.
cc[]array of objectsYesObservers. Always present; [] when there are none.
cc[].emailstring (email)YesThe observer's email address.
cc[].namestringNoThe observer's name.
fields[]array of objectsYesTag placements. May be empty when signer tags are handled externally.
fields[].typeenumYesOne of signature, date, text, name, email, title, initials, checkbox, number, editableEmail, editableDate, stamp.
fields[].roleNamestringYesThe signer this tag belongs to.
fields[].x, fields[].ynumberYesTag position on the page.
fields[].pageintegerYesPage number, 1-indexed.
fields[].width, fields[].heightnumberYesTag dimensions. Always greater than zero.
fields[].requiredbooleanYesWhether the signer must complete this tag.
fields[].pageWidth, fields[].pageHeightnumberYesDimensions of the page the tag sits on. Always greater than zero.
fields[].tagOriginembedded | placedNoHow the tag originated.
fields[].tagIdstringNoIdentifier for the tag: a seven-character lowercase alphanumeric ID over 0-9 and a-z, such as 2gbqtjl. Corresponds to the [tagId] text tag in the source document. Not present on every tag.
documentHashes[]array of stringsYesSHA-256 hex digest of each document_N part, in upload order. Expect a single entry for now, since Ironclad currently sends one document per signature request.
expiresAtstring (ISO-8601)NoExpected expiration time in UTC. Present only when a signing deadline is set.
fromNamestringNoThe sender name to present to signers.
emailSettings.subjectstringYesSubject line for the signature request email.
emailSettings.bodystringYesBody text for the signature request email.

Example:

{
  "signatureRequestId": "3f0c8b12-5e44-4c1a-9d77-2b91f0a4de21",
  "routingType": "sequential",
  "signers": [
    {
      "roleName": "Buyer",
      "name": "Kim Lam",
      "email": "[email protected]",
      "title": "",
      "routingOrder": 0
    },
    {
      "roleName": "Seller",
      "name": "Jimothy Kim",
      "email": "[email protected]",
      "title": "General Counsel",
      "routingOrder": 1
    }
  ],
  "cc": [],
  "fields": [
    {
      "type": "signature",
      "roleName": "Buyer",
      "x": 72,
      "y": 640,
      "page": 1,
      "width": 150,
      "height": 30,
      "required": true,
      "pageWidth": 612,
      "pageHeight": 792,
      "tagOrigin": "embedded",
      "tagId": "2gbqtjl"
    },
    {
      "type": "signature",
      "roleName": "Seller",
      "x": 320,
      "y": 640,
      "page": 1,
      "width": 150,
      "height": 30,
      "required": true,
      "pageWidth": 612,
      "pageHeight": 792,
      "tagOrigin": "placed"
    }
  ],
  "documentHashes": ["e3b0c44298fc1c14..."],
  "expiresAt": "2026-02-01T00:00:00.000Z",
  "fromName": "Ironclad",
  "emailSettings": {
    "subject": "Please sign: Mutual NDA",
    "body": "Your signature is requested on the attached agreement."
  }
}
📌

Signer Identity Is roleName

Ironclad's internal signer ID is never sent on the wire. roleName is unique within a request, is the key you must echo back on every inbound status push, and is what you receive on remind.

documentHashes is how document integrity is bound to the request signature: the signature covers the JSON body string, the body contains documentHashes, and each hash is the SHA-256 of the corresponding document_N part. See Document Integrity.

Expected Response

FieldTypeRequiredDescription
statuspendingYesMust be exactly pending on initiate.
signatures[]array of objectsYesThe initial state of every signer on the signature request. Signers omitted here are removed.
signatures[].roleNamestringYesMust be unique across the array and match a roleName from the request.
signatures[].signer.namestringYesThe signer's full name.
signatures[].signer.emailstring (email)YesThe signer's email address.
signatures[].statusawaiting_signature | signed | declinedYesThis signer's current state.
signatures[].declineReasonstringNoOnly when status is declined.
signatures[].firstViewedAtstring (ISO-8601)NoWhen 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.

Example:

{
  "status": "pending",
  "signatures": [
    {
      "roleName": "Buyer",
      "signer": {
        "name": "Kim Lam",
        "email": "[email protected]"
      },
      "status": "awaiting_signature"
    },
    {
      "roleName": "Seller",
      "signer": {
        "name": "Jimothy Kim",
        "email": "[email protected]"
      },
      "status": "awaiting_signature"
    }
  ]
}

Ironclad validates this response. Unknown keys are ignored rather than rejected, so a misspelled field is silently dropped, while a missing or malformed required field fails the call.

⚠️

Return the Full Signatures Array

Include an entry for every signer on the signature request, including signers whose state has not changed. Any signer omitted from the array is removed from the signature request.

Removal is permanent. A signer dropped from the array cannot be re-added to that signature request. Their roleName stops being valid, so a later status push that includes them again returns 400 rather than restoring them.

This applies to the initiate response, the status response, and every inbound status push. It is never a partial update.

signer.title and signer.routingOrder are sent to you at initiation but cannot be updated from the middleware, so they are not expected back. Omit them from signer here and on status pushes.

Status

GET {endpointUrl}/{id}/status

Returns the same response shape as Initiate, except that status may be any of the following:

ValueMeaning
pendingThe signature request is open and awaiting signer action.
completedAll required signatures have been collected.
voidedThe signature request was cancelled or otherwise terminated.

Ironclad trusts the request-level status field over any inference from individual signer states. If every signer has signed but you return pending, the signature request stays open.

As with initiate, return the full signatures array on every response. A signer omitted from the array is removed from the signature request.

Cancel

POST {endpointUrl}/{id}/cancel

Terminate the signature request with your provider.

Request body: {}

Expected response: any 2xx. The response body is ignored.

⚠️

Stop Pushing After a Successful Cancel

Once your middleware returns 2xx, Ironclad discards its record of the signature request. The ID stops existing, so a later status push for it returns 404 rather than being absorbed as a no-op.

This differs from a request you reported as completed or voided yourself, where the record is retained and further pushes are accepted as no-ops. Treat a successful cancel as the end of that ID's life and drop any queued or in-flight pushes for it.

Remind

POST {endpointUrl}/{id}/remind

Remind an outstanding signer.

FieldTypeRequiredDescription
roleNamestringNoThe signer to remind.

Ironclad only reminds signers who have not signed on an open signature request, and today always targets a single signer, so roleName is always present in practice. It is documented as optional for forward compatibility. If you ever receive it omitted, remind all outstanding signers.

Expected response: any 2xx. The response body is ignored.

Document

GET {endpointUrl}/{id}/document

Return the signed PDF with Content-Type: application/pdf. Ironclad enforces the following constraints on the response:

  • Content type must start with application/pdf, or the response is rejected.
  • Size cap of 50 MB, checked against Content-Length for a fast fail and again against the buffered body length.
  • An empty body is rejected. If your provider cannot return a document mid-signing, returning an error or empty body during a partial or preview read is tolerated, and Ironclad falls back to the unsigned packet. A read after the signature request has completed must return the signed PDF.
  • The document is antivirus-scanned on arrival. Infected or unscannable files are rejected (fail-closed).

Did this page help you?