Collect the evidence of a signed document

Circularo records everything that happens to a document, and hands it back in three forms: a timeline you can read, a sealed audit trail, and the certificate that closes a finished transaction. This guide collects all three in three calls.

Before you begin

  • A document with something to show. The examples use an agreement that was sent to a signer and signed.

Step 1: Read the timeline

The history is the machine-readable view — what happened, when, and to whom:

GET /documents/pXq4NcYBhK9tWdA2mFzR/history
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "pagination": null,
    "results": [
        {
            "actor": "jane.doe@example.com",
            "message": null,
            "occurredAt": "2026-05-11T09:36:00.000Z",
            "recipients": [
                "jane.doe@example.com"
            ],
            "type": "completed"
        },
        {
            "actor": "jane.doe@example.com",
            "message": null,
            "occurredAt": "2026-05-11T09:34:00.000Z",
            "recipients": [
                "jane.doe@example.com"
            ],
            "type": "signed"
        },
        {
            "actor": "john.smith@example.com",
            "message": "Please sign your employment agreement.",
            "occurredAt": "2026-05-11T09:32:00.000Z",
            "recipients": [
                "jane.doe@example.com"
            ],
            "type": "shared"
        },
        {
            "actor": "john.smith@example.com",
            "message": null,
            "occurredAt": "2026-05-11T09:30:00.000Z",
            "recipients": [],
            "type": "created"
        }
    ],
    "total": 4,
    ...
}

A few things to notice:

  • Events come newest first, and the whole timeline is returned at once — pagination is null, so there is nothing to page through.

  • type says what happened. The document above was created, shared with a recipient, signed by them, and then the transaction completed.

  • actor is who performed the action, or null when Circularo raised the event itself. recipients names the people an event concerns, and is empty for events about the document alone.

  • message carries the note that went with the action — here the message sent along with the invitation.

Use this when your own system needs to react to what happened, or to show a progress trail in your interface.

Step 2: Download the audit trail

The audit trail is the same story as a sealed PDF, meant to be archived or handed to somebody outside:

GET /documents/pXq4NcYBhK9tWdA2mFzR/audit-trail
Authorization: Bearer YOUR_API_KEY
HTTP/2 200
Content-Type: application/pdf

The body is the PDF itself. You can ask for it at any point in the document's life — it captures the history as it stands at that moment, so a document still being signed produces a valid, partial trail.

Note

An audit trail belongs to one document. The organization-wide record of who did what across your whole workspace is a different thing — the audit logs of Search the audit logs.

Step 3: Download the certificate of fulfillment

Once every recipient has acted, the transaction can be certified as fulfilled:

GET /documents/pXq4NcYBhK9tWdA2mFzR/certificate-of-fulfillment
Authorization: Bearer YOUR_API_KEY
HTTP/2 200
Content-Type: application/pdf

The body is a PDF again, this time a closing statement rather than a running record — which is why it exists only for a transaction that has finished. Ask for it earlier, while recipients are still acting or before the document was ever sent, and the answer is 409.

Complete example

Collecting all three, 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 documentId = "pXq4NcYBhK9tWdA2mFzR"; // the finished document

const history = await (await fetch(`${BASE_URL}/documents/${documentId}/history`, { headers: AUTH_HEADER })).json();
for (const event of history.results.slice().reverse()) {
    console.log(`${event.occurredAt} ${event.type} ${event.actor ?? "system"}`);
}

const download = async (path, target) => {
    const res = await fetch(`${BASE_URL}/documents/${documentId}/${path}`, { headers: AUTH_HEADER });
    if (!res.ok) throw new Error(`${path} failed: ${res.status}`); // error handling kept minimal for brevity
    await fs.writeFile(target, Buffer.from(await res.arrayBuffer()));
};

await download("audit-trail", "audit-trail.pdf");
await download("certificate-of-fulfillment", "certificate.pdf");

Next steps