{"openapi":"3.1.0","info":{"title":"NomaSign Integration API","version":"1.2.3","description":"Send documents for electronic signature from your own system: send reusable templates or your own generated PDFs, prefill fields, receive signing links, and get webhook notifications when documents are signed. Authenticate by exchanging your refresh token for a short-lived access token at POST /connect/token, then call the endpoints below with a Bearer token."},"paths":{"/connect/token":{"post":{"summary":"Exchange your refresh token for an access token (OAuth2)","description":"Pass your refresh token and receive a short-lived access token to use as a Bearer token on all API calls. The OAuth2 grant_type and client_id are fixed server-side and must not be sent.","tags":["auth"],"responses":{"200":{"description":"Successful response"},"400":{"description":"Validation failed"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"refresh_token":{"type":"string","minLength":1,"description":"The refresh token generated on the NomaSign Integration page."}},"required":["refresh_token"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}}}},"/api/templates/send":{"post":{"summary":"Send documents for signature using a reusable template, to one or many recipients at once","description":"Sends a template to one or more sets of recipients. This is the preferred way to send documents through the API: the template's documents are prepared once, when you design the template, so each send is a small, fast call. Uploading documents per send (`POST /api/sessions/send`) uses far more of your API allowance per call. Reach for that endpoint only when every document is unique, for example generated per transaction.\n\n**Before you call:**\n\n1. Create and test the template in the NomaSign web app ([guide](https://www.nomasign.com/api/steps/3/)), using the integration account.\n2. Open the template and click **Copy payload**. It gives you a ready-made request body for this endpoint, listing every placeholder role and fillable field with a hint per field.\n\n**What one call does**, per `signingRequests` entry:\n\n1. Your recipients fill the template's placeholder roles (matched by role label).\n2. Your optional `fields` values prefill the template's fillable fields, matched by field label. A label fills the first matching field; when several fields share a label, address the second as \"Amount 2\", the third as \"Amount 3\", and so on.\n3. A signing session is created and sent under the integration account you authenticated with.\n\nThe created sessions are visible in the web app when logged in as the integration account. Treat them as owned by your integration; editing them in the app can break what your integration expects.\n\nThe call is synchronous: the response arrives when every session has been created and sent. Base your HTTP timeout on the number and size of the template's documents and the number of `signingRequests` entries, since the documents are copied and processed for every session created.","tags":["templates"],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"instantiation":{"type":"object","properties":{"bulkSendBatchId":{"type":"string","description":"Groups the created sessions; absent for a single signingRequests entry."},"sessions":{"type":"array","items":{"type":"object","properties":{"rowIndex":{"type":"integer","description":"Index into the request's signingRequests array."},"status":{"type":"string","description":"\"created\" or \"error\"."},"sessionId":{"type":"string"},"documentIds":{"type":"array","items":{"type":"string"},"description":"Ids of every document on the created session; empty when status is \"error\"."},"error":{"type":"string","description":"Failure detail when status is \"error\"."}},"required":["rowIndex","status","documentIds"],"additionalProperties":false}}},"required":["sessions"],"additionalProperties":false},"invites":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string"},"title":{"type":"string"},"inviteStatus":{"type":"string","enum":["sent","error","skipped"]},"inviteError":{"type":"string"},"participants":{"type":"array","items":{"type":"object","properties":{"participantId":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["sent","failed","skipped"],"description":"Invite email result for this participant."},"signingUrl":{"type":"string","description":"The signing link for this participant, for you to deliver yourself when notify is false."}},"required":["participantId","email","status"],"additionalProperties":false}}},"required":["sessionId","inviteStatus","participants"],"additionalProperties":false}}},"required":["instantiation","invites"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"401":{"description":"Authentication required"},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"503":{"description":"Server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"templateId":{"type":"string","minLength":1,"description":"Id of the template to send. Copy it from the template's URL in the web app, or use the template's \"Copy payload\" button to get a complete request body."},"signingRequests":{"type":"array","items":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","minLength":1,"description":"The template placeholder role this person fills, e.g. \"Signer 1\" (matched case-insensitively)."},"name":{"type":"string","minLength":1,"description":"Signer display name, shown on the signing document."},"email":{"type":"string","format":"email","description":"Signer email address, e.g. \"thabo.nkosi@nomasign.com\"."}},"required":["label","name","email"],"additionalProperties":false},"minItems":1},"fields":{"type":"array","items":{"type":"object","properties":{"document":{"type":"string","minLength":1,"description":"Optional filter: only match fields in this document (filename)."},"label":{"type":"string","minLength":1,"description":"The template field label to prefill. A label fills the first matching field; when several fields share a label, address the second as \"Amount 2\", the third as \"Amount 3\", and so on."},"recipient":{"type":"string","minLength":1,"description":"Optional filter: only match fields assigned to this placeholder role."},"value":{"type":"string","description":"The value to prefill; checkboxes take \"true\" or \"false\"."}},"required":["label","value"],"additionalProperties":false}}},"required":["recipients"],"additionalProperties":false},"minItems":1,"description":"One entry per signing session to create, e.g. one per new hire when sending an onboarding pack."},"signingType":{"type":"string","enum":["parallel","sequential"],"description":"\"parallel\" (default): all recipients can sign at once; \"sequential\": in recipient order."},"authRequirement":{"type":"string","enum":["public","otp"],"description":"\"otp\" (default): NomaSign verifies each signer with a one-time code at signing time, so the signature is tied to a verified identity. \"public\" removes that verification: anyone who obtains the link can sign in the recipient's name, and the signature carries no identity assurance. Choose \"public\" only if your own system already guarantees who holds the link and you accept that weaker evidence."},"senderName":{"type":"string","description":"Overrides the sender name shown in invites."},"subject":{"type":["string","null"],"description":"Overrides the subject of the invite email sent from the integration account."},"message":{"type":"string","description":"Personal message included in the invite email sent from the integration account."},"messageTemplate":{"type":"string"},"cc":{"type":"array","items":{"type":"string"},"description":"Extra addresses copied on the invite email."},"replyToEmail":{"type":"string","format":"email"},"sendInitialNotification":{"type":"boolean","default":false,"description":"Sequential signing only: also notify later signers up front that their turn is coming."},"remindersEnabled":{"type":"boolean","default":false,"description":"Send reminder emails from the integration account before expiry (see reminderDaysBeforeExpiry)."},"reminderDaysBeforeExpiry":{"type":"array","items":{"type":"integer","minimum":0}},"expiresInDays":{"type":"integer","exclusiveMinimum":0},"notify":{"type":"boolean","default":true,"description":"True (default): the invites and signing-lifecycle updates are emailed from the integration account. Set false to send no emails and deliver the signing links yourself; the one-time code at signing time is always sent by NomaSign."}},"required":["templateId","signingRequests"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}}}},"/api/sessions/send":{"post":{"summary":"Send your own PDF documents for signature and get a signing link for each signer","description":"Creates signing sessions from PDFs in your connected cloud drive and returns one signing link per signer. Use this when every document is unique, for example generated per transaction. If you send the same document pack repeatedly, prefer `POST /api/templates/send`: templates are prepared once when you design them, so each send is a smaller, faster call that uses far less of your API allowance.\n\n**Before you call:**\n\n1. Upload the PDFs to the cloud drive connected to the integration account and keep the file ids the provider returns.\n2. That is all. Signature fields are placed automatically: initials for every signer on every page, plus a final signature page.\n\n**What one call does:**\n\n1. The referenced PDFs are downloaded and prepared for signing, once for all entries.\n2. Each `signingRequests` entry creates its own session from those documents under the integration account you authenticated with, so one call can send the same pack to several independent signer groups.\n3. Each session is sent and the response returns a signing link per signer for you to deliver through your own channels.\n\nEntries are independent: if some fail, the response is `207` with per-row results. Call again with only the failed entries, and keep the source files until every entry has succeeded. If the documents themselves are rejected (encrypted, XFA, or unreadable PDFs), nothing is created and the response lists the files to fix.\n\nThe created sessions are visible in the web app when logged in as the integration account. Treat them as owned by your integration; editing them in the app can break what your integration expects.\n\nThe call is synchronous: the response arrives when every session has been created and sent. Base your HTTP timeout on the number and size of the referenced files and the number of `signingRequests` entries, since every document is downloaded, prepared, and uploaded per session.","tags":["sessions"],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"row":{"type":"integer","description":"Index into the request's signingRequests array."},"status":{"type":"string","enum":["sent","failed"]},"sessionId":{"type":"string"},"documents":{"type":"array","items":{"type":"object","properties":{"documentId":{"type":"string"},"cloudFileId":{"type":["string","null"],"description":"The source file id this document was created from."},"filename":{"type":"string"},"sourceFileSafeToDelete":{"type":"boolean","description":"True only when the whole call succeeded. A 207 retry re-downloads the sources, so keep them until then."}},"required":["documentId","cloudFileId","filename","sourceFileSafeToDelete"],"additionalProperties":false}},"participants":{"type":"array","items":{"type":"object","properties":{"participantId":{"type":"string"},"email":{"type":"string"},"displayName":{"type":"string"},"signingUrl":{"type":["string","null"],"description":"The signing link for this signer, for you to deliver through your own channel."}},"required":["participantId","email","signingUrl"],"additionalProperties":false}},"error":{"type":"string","description":"Machine-readable failure reason (failed rows only)."},"message":{"type":"string","description":"Human-readable failure detail (failed rows only)."}},"required":["row","status"],"additionalProperties":false}}},"required":["rows"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"207":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"row":{"type":"integer","description":"Index into the request's signingRequests array."},"status":{"type":"string","enum":["sent","failed"]},"sessionId":{"type":"string"},"documents":{"type":"array","items":{"type":"object","properties":{"documentId":{"type":"string"},"cloudFileId":{"type":["string","null"],"description":"The source file id this document was created from."},"filename":{"type":"string"},"sourceFileSafeToDelete":{"type":"boolean","description":"True only when the whole call succeeded. A 207 retry re-downloads the sources, so keep them until then."}},"required":["documentId","cloudFileId","filename","sourceFileSafeToDelete"],"additionalProperties":false}},"participants":{"type":"array","items":{"type":"object","properties":{"participantId":{"type":"string"},"email":{"type":"string"},"displayName":{"type":"string"},"signingUrl":{"type":["string","null"],"description":"The signing link for this signer, for you to deliver through your own channel."}},"required":["participantId","email","signingUrl"],"additionalProperties":false}},"error":{"type":"string","description":"Machine-readable failure reason (failed rows only)."},"message":{"type":"string","description":"Human-readable failure detail (failed rows only)."}},"required":["row","status"],"additionalProperties":false}}},"required":["rows"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"401":{"description":"Authentication required"},"422":{"description":"Status 422","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"failedDocuments":{"type":"array","items":{"type":"object","properties":{"cloudFileId":{"type":["string","null"]},"filename":{"type":"string"},"reason":{"type":"string","description":"Machine-readable rejection reason, e.g. encrypted | xfa | load-failed."},"message":{"type":"string"}},"required":["cloudFileId","filename","reason","message"],"additionalProperties":false},"description":"Present when documents were rejected before any session was created."},"rows":{"type":"array","items":{"type":"object","properties":{"row":{"type":"integer","description":"Index into the request's signingRequests array."},"status":{"type":"string","enum":["sent","failed"]},"sessionId":{"type":"string"},"documents":{"type":"array","items":{"type":"object","properties":{"documentId":{"type":"string"},"cloudFileId":{"type":["string","null"],"description":"The source file id this document was created from."},"filename":{"type":"string"},"sourceFileSafeToDelete":{"type":"boolean","description":"True only when the whole call succeeded. A 207 retry re-downloads the sources, so keep them until then."}},"required":["documentId","cloudFileId","filename","sourceFileSafeToDelete"],"additionalProperties":false}},"participants":{"type":"array","items":{"type":"object","properties":{"participantId":{"type":"string"},"email":{"type":"string"},"displayName":{"type":"string"},"signingUrl":{"type":["string","null"],"description":"The signing link for this signer, for you to deliver through your own channel."}},"required":["participantId","email","signingUrl"],"additionalProperties":false}},"error":{"type":"string","description":"Machine-readable failure reason (failed rows only)."},"message":{"type":"string","description":"Human-readable failure detail (failed rows only)."}},"required":["row","status"],"additionalProperties":false},"description":"Present when every row failed."}},"required":["error","message"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"502":{"description":"Server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"503":{"description":"Server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cloudFileIds":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"description":"File ids of the PDFs in your connected cloud drive, i.e. the ids the provider returned when you uploaded them."},"title":{"type":"string","minLength":1,"description":"Session title, used verbatim for every session; defaults to the first file's name."},"signingRequests":{"type":"array","items":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Signer display name, e.g. \"Thabo Nkosi\"."},"email":{"type":"string","format":"email","description":"Signer email address, e.g. \"thabo.nkosi@nomasign.com\"."},"order":{"type":"integer","exclusiveMinimum":0,"description":"1-based signing order for sequential signing; omit for parallel."}},"required":["name","email"],"additionalProperties":false},"minItems":1,"description":"The signers of this session. Every generated field is assigned to one of them."}},"required":["recipients"],"additionalProperties":false},"minItems":1,"description":"One entry per session to create. The same documents can go to several signer groups in one call."},"signingType":{"type":"string","enum":["parallel","sequential"],"default":"parallel","description":"\"parallel\" (default): all recipients can sign at once; \"sequential\": in recipient order."},"authRequirement":{"type":"string","enum":["public","otp"],"default":"otp","description":"\"otp\" (default): NomaSign verifies each signer with a one-time code at signing time, so the signature is tied to a verified identity. \"public\" removes that verification: anyone who obtains the link can sign in the recipient's name, and the signature carries no identity assurance. Choose \"public\" only if your own system already guarantees who holds the link and you accept that weaker evidence."},"expiresInDays":{"type":"integer","exclusiveMinimum":0,"default":30},"notify":{"type":"boolean","default":false,"description":"False (default): no emails are sent; you deliver the returned signing links and updates yourself. True: the invites and signing-lifecycle emails (next-signer invites, signed and completed updates) are sent from the integration account. The one-time code at signing time is always sent by NomaSign."}},"required":["cloudFileIds","signingRequests"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}}}},"/api/sessions/cancel":{"post":{"summary":"Cancel a signing request and revoke its signing links","description":"Cancels a signing session and revokes every outstanding signing link, so recipients who have not signed yet can no longer open it. Use this when a deal falls through or documents must be reissued.\n\nIf the session was sent with `notify: true`, a cancellation email is sent from the integration account to the participants, including your `reason` if given. A session sent silently is cancelled silently: you inform the participants through your own channels.\n\nCancelling is idempotent: a session that was already cancelled, expired, or declined returns `200` with `alreadyCancelled: true`, so retrying after a timeout is safe. A session every signer has completed can no longer be cancelled; that returns `409` and the signed documents stand.","tags":["sessions"],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"sessionId":{"type":"string"},"cancelled":{"type":"boolean","description":"Always true on 200: the session has no live signing links."},"alreadyCancelled":{"type":"boolean","description":"True when there was nothing left to revoke (cancelled earlier, expired, or a signer declined). Retrying a cancel is safe; it returns 200 again with this flag set."}},"required":["sessionId","cancelled","alreadyCancelled"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"401":{"description":"Authentication required"},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"409":{"description":"Status 409","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"502":{"description":"Server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}},"503":{"description":"Server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"cloudFileId":{"type":"string"}},"required":["error"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}}},"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","description":"The session to cancel, i.e. a `rows[].sessionId` returned by POST /api/sessions/send."},"reason":{"type":"string","minLength":1,"maxLength":500,"description":"Optional cancellation reason. Recorded on the session and included in the cancellation email sent from the integration account when the session was sent with `notify: true`."}},"required":["sessionId"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}}}}}},"components":{"schemas":{},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Access token from POST /connect/token. Exchange your refresh token there, then click Authorize and paste the access_token value."}}}}