In this guide you will send a PDF to a recipient for signature, track the transaction until everyone has signed, and download the signed result. Three endpoints do the whole job, and you call the tracking one again until the transaction is done.
Before you begin
You will need:
-
An API key. Create one in the Circularo application under My Account → Manage API keys, or ask your administrator for one. Every request below authenticates with the
Authorization: Bearer YOUR_API_KEYheader. -
A PDF file to send for signature. Any PDF works; this guide uses
employment-agreement.pdf.
All paths in this guide are relative to your instance's base URL — the examples use:
https://sandbox.circularo.com/api/v1/public
Every Circularo environment — production, or a testing instance if you have one — has its own base URL and its own API keys. Point the examples at the environment your key belongs to.
Step 1: Create the transaction
A transaction is the signing or approval process of one document. A single call creates the document from your PDF and sends it to the recipient, with a signature field placed where they should sign:
POST /transactions
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"document": {
"title": "Employment Agreement",
"fileContentBase64": "JVBERi0xLjQKJeLjz9MK..."
},
"send": {
"recipients": [
{
"recipient": "jane.doe@example.com",
"purpose": "sign"
}
],
"signatureFields": [
{
"recipients": [
"jane.doe@example.com"
],
"type": "signature",
"pages": [
1
],
"position": {
"x": 0.6,
"y": 0.75,
"width": 0.25,
"height": 0.08
}
}
]
}
}
A few things to notice:
-
documentcreates the document inline — atitleand the file content are all it needs. ThefileContentBase64value is your PDF encoded as base64 (shortened here); for large or reused files you can upload first and pass afileIdinstead — see Upload and download files. -
send.recipientslists who has to act.purposeissign, so the recipient has to sign the document — other purposes let a recipient approve, review, accept, or just view it. A plain e-mail address is enough; the recipient does not need a Circularo account, as long as your organization allows sending outside it. -
send.signatureFieldsplaces a field on the page for a recipient to fill.typeissignature, so this is where they sign. Thepositionvalues are fractions of the page size (x: 0.6means 60 % from the left edge), so they work for any page format.
HTTP/2 201
{
"id": "Vt7RfKq2LmXe5ZwGyB4N",
"status": "in-progress",
"document": {
"id": "pXq4NcYBhK9tWdA2mFzR",
"mainFileId": "2f7d6c0a9b1e483fa5d3c8b41e6f90aa",
"metadataDefinition": "d_default",
"signatureProvider": null,
"templateId": null,
"title": "Employment Agreement"
},
"recipients": [
{
"completedAt": null,
"expiresAt": null,
"id": "Gk5cPdM9XvT3qHnU7jWs",
"isMandatory": true,
"language": null,
"message": null,
"order": null,
"permissions": [
"print",
"viewMetadata",
"annotateOnReject",
"rejectWithoutComment",
"uploadFiles"
],
"purpose": "sign",
"quorum": null,
"recipient": "jane.doe@example.com",
"reminder": null,
"status": "pending",
"turnStartedAt": "2026-05-11T09:30:00.000Z",
"verification": null
}
],
"webUrl": "https://sandbox.circularo.com/home/detail?select=pXq4NcYBhK9tWdA2mFzR",
...
}
The transaction is now in-progress and the recipient's participation is pending — Circularo has e-mailed them an invitation to open and sign the document. Keep the transaction id for tracking, and note webUrl: a ready-made link to the document detail in the Circularo application you can show to your own users.
Step 2: Track the progress
Read the transaction at any time to see where it stands:
GET /transactions/Vt7RfKq2LmXe5ZwGyB4N
Authorization: Bearer YOUR_API_KEY
HTTP/2 200
{
"id": "Vt7RfKq2LmXe5ZwGyB4N",
"status": "in-progress",
"completedAt": null,
"recipients": [
{
"completedAt": null,
"expiresAt": null,
"id": "Gk5cPdM9XvT3qHnU7jWs",
"isMandatory": true,
"language": null,
"message": null,
"order": null,
"permissions": [
"print",
"viewMetadata",
"annotateOnReject",
"rejectWithoutComment",
"uploadFiles"
],
"purpose": "sign",
"quorum": null,
"recipient": "jane.doe@example.com",
"reminder": null,
"status": "pending",
"turnStartedAt": "2026-05-11T09:30:00.000Z",
"verification": null
}
],
...
}
The transaction status stays in-progress until everyone acts, and each recipient carries their own status alongside. Polling this endpoint is fine to start with — for production integrations, register a webhook and be notified about changes instead, as React to events with webhooks shows.
completed is not the only ending. A recipient who refuses ends the transaction as rejected for everybody, a deadline that passes as expired, and calling it off yourself leaves it cancelled. All four are final, so wait for status to stop being in-progress rather than for the one you hoped for.
Step 3: Collect the result
Once the recipient signs, the same read shows the finished transaction:
GET /transactions/Vt7RfKq2LmXe5ZwGyB4N
Authorization: Bearer YOUR_API_KEY
HTTP/2 200
{
"id": "Vt7RfKq2LmXe5ZwGyB4N",
"status": "completed",
"completedAt": "2026-05-11T09:34:00.000Z",
"document": {
"id": "pXq4NcYBhK9tWdA2mFzR",
"mainFileId": "6c1e83b5f7a9042dcb8e5f217a3d90c4",
"metadataDefinition": "d_default",
"signatureProvider": "internal",
"templateId": null,
"title": "Employment Agreement"
},
"recipients": [
{
"completedAt": "2026-05-11T09:32:00.000Z",
"expiresAt": null,
"id": "Gk5cPdM9XvT3qHnU7jWs",
"isMandatory": true,
"language": null,
"message": null,
"order": null,
"permissions": [
"print",
"viewMetadata",
"annotateOnReject",
"rejectWithoutComment",
"uploadFiles"
],
"purpose": "sign",
"quorum": null,
"recipient": "jane.doe@example.com",
"reminder": null,
"status": "completed",
"turnStartedAt": "2026-05-11T09:30:00.000Z",
"verification": null
}
],
...
}
Note document.mainFileId: the signed PDF — with the signature and Circularo's evidence embedded — is a new file (signing never rewrites the content you sent).
Step 4: Download the signed PDF
The file's content endpoint returns the binary:
GET /files/6c1e83b5f7a9042dcb8e5f217a3d90c4/content
Authorization: Bearer YOUR_API_KEY
HTTP/2 200
Content-Type: application/pdf
Store the PDF wherever your process needs it — it is the legally meaningful artifact of the whole flow.
Complete example
The whole flow as a Node.js script. Error handling is minimal for brevity — in production, inspect the error envelope of non-2xx responses.
import fs from "node:fs/promises";
const BASE_URL = "https://sandbox.circularo.com/api/v1/public"; // your instance's base URL
const API_KEY = "YOUR_API_KEY";
const AUTH_HEADER = { "Authorization": `Bearer ${API_KEY}` };
// Step 1: create the transaction and send it for signature
const pdfBase64 = (await fs.readFile("employment-agreement.pdf")).toString("base64");
const createRes = await fetch(`${BASE_URL}/transactions`, {
method: "POST",
headers: { ...AUTH_HEADER, "Content-Type": "application/json" },
body: JSON.stringify({
document: {
title: "Employment Agreement",
fileContentBase64: pdfBase64
},
send: {
recipients: [
{
recipient: "jane.doe@example.com",
purpose: "sign"
}
],
signatureFields: [
{
recipients: [
"jane.doe@example.com"
],
type: "signature",
pages: [
1
],
position: {
x: 0.6,
y: 0.75,
width: 0.25,
height: 0.08
}
}
]
}
})
});
if (!createRes.ok) throw new Error(`Transaction failed: ${createRes.status}`); // error handling kept minimal for brevity
const transactionId = (await createRes.json()).id;
// Step 2: poll until the transaction reaches a final state
let transaction;
do {
await new Promise((resolve) => setTimeout(resolve, 30_000));
transaction = await (await fetch(`${BASE_URL}/transactions/${transactionId}`, { headers: AUTH_HEADER })).json();
} while (transaction.status === "in-progress");
console.log(`Transaction finished: ${transaction.status}`);
// Step 3: download the signed PDF
if (transaction.status === "completed") {
const content = await fetch(`${BASE_URL}/files/${transaction.document.mainFileId}/content`, { headers: AUTH_HEADER });
await fs.writeFile("employment-agreement-signed.pdf", Buffer.from(await content.arrayBuffer()));
}
Next steps
-
Handle errors, retries, and pagination like a production integration — Robust Integrations.
-
Hand out signing links yourself, add more recipients, form fields, or identity verification — Signing & Approvals covers every variant of sending.
-
Upload and reuse files, search documents, and collect evidence — Documents & Files.