Automation and webhooks
Starting a SignPlus signing or exporting a PDF from an automation rule, and the two webhooks SignPlus sends when a signing completes or a page is edited.
What the video shows
- The title: Start a signing from Jira.
- In a Jira automation rule, add an action, search for signplus and choose Start a SignPlus signing under Other apps.
- The first time, select Connect. A flag says the connection is created.
- Choose the Legal space, type part of the page title and pick Service agreement.
- Under Signers, select Mari Tamm and select Add signer.
- Select Save and enable. The rule reads Enabled.
- Oktul, Atlassian Marketplace Partner.
A rule can start a signing, export a PDF, and react when a signing completes or a signed page changes.
Which product, and what that changes
The SignPlus actions appear in Jira automation, where Atlassian publishes the module. Typical use: a work item moves to Approved and its Confluence page goes out for signature.
In Confluence, use Send web request with the REST API, on any plan.
| You want to | In Jira | In Confluence | In Code Studio |
|---|---|---|---|
| Start a signing | The SignPlus component | Send web request | http.post to the REST API |
| Export a PDF | The SignPlus component | Send web request | http.post to the REST API |
| React when a signing completes | Incoming webhook trigger | Incoming webhook trigger | An API trigger |
| React when a signed page is edited | Incoming webhook trigger | Incoming webhook trigger | An API trigger |
The Jira components need SignPlus connected to Jira. See connecting Jira.
What a rule acts as
SignPlus itself. Page history and logs name SignPlus, not a person. Page restrictions apply to the app like anyone else. A rule acts only in the spaces a Confluence administrator names under Spaces automation rules can use in the global settings. With none named, every rule stops and says why.
Add SignPlus to a Jira flow
In the Jira flow builder, add an action and pick Start a SignPlus signing or Export a page as PDF (under Other apps).
- Connect it, once
The first time, select Connect. Connection created confirms it.
- Choose the page
Pick the Space, then type part of the page title.
- Fill in the rest
Empty fields follow the space’s settings.
- Save the flow
Use Save and enable in the flow builder.
Choosing the page
Use the pickers for a fixed page, such as a policy. To take the page from the work item, turn on
Enter a page ID or smart value instead, for example {{issue.customfield_10050}}.
Start a signing from a flow
| Section | What it holds |
|---|---|
| Page | The page, picked or given as an id or smart value |
| Signers | Everyone invited when the flow runs |
| Document | Document name, Format and Message to signers. An empty name uses the page title |
| Deadline | When the signing should be finished, if at all |
| Page access while it is signed | Make the page read-only, Restrict page viewing to signers and viewers, Include attachments. The space may overrule each |
Signers
Choose Who to add (Internal user, External partner or Group), the Role, then Add signer.
For signers known only at run time, turn on More signers from smart values: comma-separated
addresses, Name <address>, and [viewer] for a viewer. For example
{{issue.reporter.displayName}} <{{issue.reporter.emailAddress}}>.
Deadline
Pick a date and time, or select Advanced for a smart value, such as a week from now:
{{now.plusDays(7).format("yyyy-MM-dd'T'HH:mmXXX")}}It must include an offset, such as 2026-12-31T17:00+02:00, and be in the future. Empty leaves it
to the space.
What later steps can read
Smart values this action returns, at the foot of the step, lists them:
{{triggeredConfluenceSigning.reference}} the signing reference{{triggeredConfluenceSigning.status}} "pending", or "queued" for a large signing{{triggeredConfluenceSigning.signerCount}} how many people were asked{{triggeredConfluenceSigning.documentName}} the name the document was given{{triggeredConfluenceSigning.documentUrl}} the document that was SENT for signing{{triggeredConfluenceSigning.documentBase64}} the same, as base64, up to 4MB{{triggeredConfluenceSigning.documentDigest}} its SHA-256, as given to DokobitdocumentUrl is not signed yet. It is the sent-for-signing-….pdf attached to the page. For
the signed file, use the completion webhook.
A group or more than 100 people runs in the background: status is queued and the reference and
document fields are empty until the completion webhook.
The same from Confluence
Add Send web request:
| Field | Value |
|---|---|
| Web request URL | <your SignPlus address>/v1/signings |
| Method | POST |
| Headers | Authorization: Bearer <token> and Content-Type: application/json |
| Body | Custom data, below |
| Wait for response | Ticked |
{ "pageId": "{{page.id}}", "signers": [ { "email": "{{initiator.email}}", "name": "{{initiator.displayName}}" } ], "name": "{{page.title}}"}Later steps read {{webhookResponse.status}} (202) and {{webhookResponse.body.jobId}}. The
reference arrives with the completion webhook or from
GET /v1/pages/{pageId}/signing-jobs/{jobId}. For the address and token, see
REST API.
Without it, webhookResponse is empty. Tokens expire after an hour, so fetch a fresh one first.
Export a PDF from a flow
Export a page as PDF renders the page with its space’s styling and attaches it. It takes the page, a Document name (default: the page title) and Include attachments (see attachments). Later steps can read:
{{createdConfluencePdf.downloadUrl}} where it is attached in Confluence{{createdConfluencePdf.attachmentName}} the filename{{createdConfluencePdf.sizeBytes}} how big it is{{createdConfluencePdf.contentBase64}} the bytes, up to 4MB, empty above thatFrom Confluence, send a POST to <your SignPlus address>/v1/pages/{{page.id}}/pdf?inline=true
with the same headers and {"name":"{{page.title}}"} as the body, and read
{{webhookResponse.body.downloadUrl}}.
Prefer the URL to contentBase64. The base64 is the whole document, and it stays in the audit
log. The URL needs the recipient’s own Confluence access.
Webhooks
Set both on Webhooks in the global settings. They work the same with Jira and Confluence.
When a signing completes
Sent once the signed file is attached, and carries it.
- In Jira or Confluence, create a rule with an Incoming webhook trigger. Atlassian gives you a URL and a secret.
- Add the actions you want, then publish the rule. An unpublished rule receives nothing.
- In SignPlus, open Webhooks, then When a signing completes. Turn on Send this notification and paste the URL under Address.
- Add a header named
X-Automation-Webhook-Tokenwith the secret as its value. - Leave Include the signed document as base64 on, unless you only want the link.
- Save, and approve the address in the dialog Atlassian shows.
Add an address adds receivers, up to 10, each with its own headers and base64 switch.
The rule then receives:
{ "event": "signing-completed", "pageId": "123456", "pageTitle": "Supplier terms", "pageUrl": "https://your-site.atlassian.net/wiki/spaces/.../pages/123456", "spaceName": "Legal", "reference": "a3f9c2d1...", "documentName": "Supplier agreement.pdf", "documentUrl": "https://your-site.atlassian.net/wiki/download/attachments/123456/Supplier%20agreement.pdf?version=1&api=v2", "documentBase64": "JVBERi0xLjcK...", "sizeBytes": 184320, "signedVersion": 7, "completedAt": "2026-09-19T09:24:00.000Z", "progress": { "total": 1, "required": 1, "signed": 1, "declined": 0 }, "signers": [ { "name": "Anna Tamm", "status": "signed", "signedAt": "2026-09-19T09:20:00.000Z" } ], "signersTruncated": false}- Sent once per signing.
signerslists the first 500; past thatsignersTruncatedistrue, and the REST API lists the rest. - No email addresses. But the signed document holds each certificate, and an Estonian one includes the personal code. Turn base64 off for receivers that should not hold it.
- Above 4MB,
documentBase64isnull; the URL remains.
Receive it in Code Studio or a Forge app
An Oktul Code Studio API trigger runs a script with the webhook’s JSON as its event, so the
script reads event.reference and event.documentUrl directly.
- In Code Studio, open API triggers and create one for the script below. Copy its address and
its secret. For a Forge app, declare a web trigger, deploy, and
forge webtriggerprints its address; choose a secret and store it withforge variables set --encrypt SIGNPLUS_WEBHOOK_SECRET <secret>. - In SignPlus, open Webhooks, then When a signing completes, and paste the address under Address.
- Add a header named
Authorizationwith the valueBearerfollowed by the secret. - Save, and approve the address in Atlassian’s dialog.
The curl tab sends a test notification to the receiver before SignPlus sends a real one.
interface SigningCompleted { event: 'signing-completed'; pageId: string; reference: string; documentName: string; documentUrl: string; completedAt: string; progress: { total: number; signed: number };}
export default async function (event: SigningCompleted) { if (event.event !== 'signing-completed') return { statusCode: 204 }; await store.set(`signed:${event.pageId}`, { reference: event.reference, documentUrl: event.documentUrl, completedAt: event.completedAt, }); log.info(`${event.documentName}: ${event.progress.signed} of ${event.progress.total} signed`); return { statusCode: 204 };}import { timingSafeEqual } from 'node:crypto';
const expected = Buffer.from(`Bearer ${process.env.SIGNPLUS_WEBHOOK_SECRET}`);
export async function receive(request) { const given = Buffer.from(request.headers.authorization?.[0] ?? ''); if (given.length !== expected.length || !timingSafeEqual(given, expected)) { return { statusCode: 401, headers: {}, body: '' }; } const notice = JSON.parse(request.body); if (notice.event === 'signing-completed') { console.log(`Signing ${notice.reference}: ${notice.progress.signed} of ${notice.progress.total} signed`); } return { statusCode: 204, headers: {}, body: '' };}curl -X POST "$RECEIVER_URL" \ -H "Authorization: Bearer $RECEIVER_SECRET" \ -H "Content-Type: application/json" \ -d '{ "event": "signing-completed", "pageId": "123456", "reference": "a3f9c2d1e4b5a6978877665544332211", "documentName": "Supplier agreement.pdf", "documentUrl": "https://your-site.atlassian.net/wiki/download/attachments/123456/Supplier%20agreement.pdf", "completedAt": "2026-09-19T09:24:00.000Z", "progress": { "total": 1, "required": 1, "signed": 1, "declined": 0 } }'The script keeps the signed document’s link per page in Code Studio’s store, where another script
reads it with store.get('signed:123456'). Turn Include the signed document as base64 off for
a Code Studio receiver: an API trigger refuses a request over 200 KB, and the link is enough to
fetch the document.
When a signed page is edited
Set up the same way under When a signed page is edited:
{ "event": "signed-page-modified", "pageId": "123456", "pageTitle": "Supplier terms", "pageUrl": "https://your-site.atlassian.net/wiki/spaces/.../pages/123456", "spaceName": "Legal", "reference": "a3f9c2d1...", "signingStatus": "completed", "signedVersion": 7, "currentVersion": 8, "modifiedAt": "2026-09-19T11:02:00.000Z"}Identifiers and versions only. A useful rule notifies the space owner and starts a fresh signing.
A Forge app can only reach hosts it declares, so Atlassian asks your administrator to approve each webhook host. Cancel, and nothing is saved. Withdraw approval later in Atlassian Administration.
When something does not work
| What you see | Usually |
|---|---|
| The SignPlus action is not in the Confluence rule builder | Expected. The components are Jira’s; use Send web request in Confluence |
| The SignPlus action is not in the Jira flow builder either | SignPlus is not connected to Jira yet. See connecting Jira |
| The step asks to connect | Select Connect. Atlassian asks before SignPlus can be used in a flow for the first time |
| The Page picker lists nothing | You cannot see any page in that space, or nothing matches what you typed. Turn on Enter a page ID or smart value instead to give the id |
| The rule runs and nothing happens | Wait for response is not ticked, or the rule is not published |
| SignPlus cannot add attachments to that page or SignPlus is not allowed to edit this page in the rule’s audit log | SignPlus cannot edit that page. Check the page restrictions |
401 from a web request |
The token has expired. They last an hour |
400 about a format, a message or a deadline |
The space’s SignPlus settings forbid what the rule asked for |
| The webhook never arrives | The address was not approved in Atlassian’s dialog, or the receiving rule is not published |
{{webhookData.documentBase64}} is empty |
The document is over 4MB, or Include the signed document as base64 is off |
| The signed document is watermarked | The site is in test mode. See signing mode |
The rule’s audit log shows each step. SignPlus’s side is on Logs in the global settings.
What a rule cannot do
- Sign on somebody’s behalf. A rule sends the request; only the signer can sign.
- Get a signed document when a signing starts. There is nothing signed yet.
- Reach a page SignPlus itself cannot.
- Overrule the space’s settings. A rule is checked against them every time.
Related
A problem or a question? Write to support@oktul.com or open a request in the Help Center (opens in a new tab).