Send a document to several recipients in order

Real agreements rarely involve one person. This guide sends a document to two recipients who act one after another — a signer first, then an approver — and watches the transaction move from one to the next. It takes one call to send and one to track.

Before you begin

Step 1: Send to a signer, then an approver

List every recipient in send.recipients. Set sequential to true so they act in rounds rather than all at once:

POST /transactions
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
    "document": {
        "title": "Employment Agreement",
        "fileContentBase64": "JVBERi0xLjQKJeLjz9MK..."
    },
    "send": {
        "sequential": true,
        "message": "Please review and sign the renewed employment agreement.",
        "recipients": [
            {
                "recipient": "jane.doe@example.com",
                "purpose": "sign",
                "order": 1,
                "message": "Please sign by Friday.",
                "expiresAt": "2026-05-15T16:00:00.000Z"
            },
            {
                "recipient": "robert.brown@example.com",
                "purpose": "approve",
                "order": 2
            }
        ],
        "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:

  • Each recipient has a purpose. The first is asked to sign; the second to approve — approvers accept the document without signing it. A transaction must ask at least one recipient to act.

  • order places the recipients in rounds. The signer acts in the first round; the approver waits in the second. Recipients that share an order act together in one round.

  • message sets a note for everyone, and a per-recipient message adds a private one. expiresAt gives the signer a personal deadline.

  • signatureFields places a signing box for the signer only. The approver signs nothing, so no field is placed for them.

HTTP/2 201

{
    "id": "Vt7RfKq2LmXe5ZwGyB4N",
    "status": "in-progress",
    "sequential": true,
    "message": "Please review and sign the renewed employment agreement.",
    "recipients": [
        {
            "id": "Gk5cPdM9XvT3qHnU7jWs",
            "recipient": "jane.doe@example.com",
            "purpose": "sign",
            "order": 1,
            "status": "pending",
            "message": "Please sign by Friday.",
            "expiresAt": "2026-05-15T16:00:00.000Z",
            ...
        },
        {
            "id": "Lp2WdN8qYt5vHmC4bRfZ",
            "recipient": "robert.brown@example.com",
            "purpose": "approve",
            "order": 2,
            "status": "queued",
            "message": null,
            "expiresAt": null,
            ...
        }
    ],
    ...
}

The response echoes the transaction id at the top — keep it, every later call in this guide uses it. The transaction is in-progress. The signer is pending — it is their turn, and Circularo has e-mailed them — while the approver is queued, waiting for the round before theirs to finish.

Note

To let several people act together in one round, give them the same order. Set a quorum on that round to let a subset suffice — for example, any two of three approvers. Recipients sharing a round must agree on the quorum.

Step 2: Watch the transaction advance

Once the signer signs, read the transaction again:

GET /transactions/Vt7RfKq2LmXe5ZwGyB4N
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "id": "Vt7RfKq2LmXe5ZwGyB4N",
    "status": "in-progress",
    "recipients": [
        {
            "recipient": "jane.doe@example.com",
            "purpose": "sign",
            "order": 1,
            "status": "completed",
            "completedAt": "2026-05-11T09:32:00.000Z",
            ...
        },
        {
            "recipient": "robert.brown@example.com",
            "purpose": "approve",
            "order": 2,
            "status": "pending",
            "completedAt": null,
            ...
        }
    ],
    ...
}

The signer is now completed, and the approver has become pending — the second round has begun automatically. The transaction stays in-progress until every recipient whose action is required has acted.

Step 3: See the transaction complete

Once the approver acts too, read the transaction one last time:

GET /transactions/Vt7RfKq2LmXe5ZwGyB4N
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "id": "Vt7RfKq2LmXe5ZwGyB4N",
    "status": "completed",
    "recipients": [
        {
            "recipient": "jane.doe@example.com",
            "purpose": "sign",
            "order": 1,
            "status": "completed",
            "completedAt": "2026-05-11T09:32:00.000Z",
            ...
        },
        {
            "recipient": "robert.brown@example.com",
            "purpose": "approve",
            "order": 2,
            "status": "completed",
            "completedAt": "2026-05-11T09:34:00.000Z",
            ...
        }
    ],
    ...
}

With both required actions done, the transaction status is completed and both recipients are completed. This is a terminal state — the document is now fully signed and approved, and its evidence trail is complete. A recipient who refuses ends the transaction instead, as rejected; either way the terminal status is what tells you the process is over.

Complete example

Sending to a routed set of recipients as a Node.js script. Error handling is minimal for brevity — in production, inspect the error envelope of non-2xx responses.

JavaScript
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}` };

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: {
            sequential: true,
            message: "Please review and sign the renewed employment agreement.",
            recipients: [
                {
                    recipient: "jane.doe@example.com",
                    purpose: "sign",
                    order: 1,
                    message: "Please sign by Friday.",
                    expiresAt: "2026-05-15T16:00:00.000Z"
                },
                {
                    recipient: "robert.brown@example.com",
                    purpose: "approve",
                    order: 2
                }
            ],
            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 transaction = await createRes.json();
for (const recipient of transaction.recipients) {
    console.log(`Round ${recipient.order}: ${recipient.recipient} (${recipient.purpose}) is ${recipient.status}`);
}

Next steps