Sign a document yourself

Some documents need no signature but your own — a policy acknowledgement, an invoice, an internal approval. Signing one yourself is the same call that sends a document for signature, with the recipients left out: a single request. The step before it, looking up the signature to sign with, is worth doing once and remembering.

Before you begin

  • Your signature image is configured in the Circularo web app. Signing uses an image that was set up as a signature there; a file you upload through the API cannot be used for one. The first step reads what you have.

  • Signing is enabled on your own account. Applying a signature needs use_sign on top of the permission to start a transaction — without it the call is refused with 403, even though everything else about it is valid.

Step 1: Look up your signature

GET /me reports the signature and initials images configured on your account:

GET /me
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "id": "john.smith@example.com",
    "signatures": [
        {
            "fileId": "da09a47b4f5b1xv726cfm36l8k1hkxzwmj5mszvfq6szoambdvc44v6i8hrhnu1f",
            "name": "My signature"
        }
    ],
    "initials": [
        {
            "fileId": "f3a91c7e5b2d48a06c1e93f7b45d20c8ae76d31b95f0c4a28e6b1d7f30a95c62",
            "name": "initials"
        }
    ],
    ...
}

Keep the fileId of the one you want to sign with. You can leave it out when signing, in which case Circularo uses the first configured image of the matching kind — naming it explicitly is worth it as soon as an account has more than one.

Step 2: Sign the document

Send the document with an apply block and no recipients. Everything else is what you already know from sending — the document can be uploaded inline like this, or referenced by documentId if it already exists:

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

{
    "document": {
        "title": "Internal Policy Acknowledgement",
        "fileContentBase64": "JVBERi0xLjQKJeLjz9MK..."
    },
    "apply": {
        "signatures": [
            {
                "fileId": "da09a47b4f5b1xv726cfm36l8k1hkxzwmj5mszvfq6szoambdvc44v6i8hrhnu1f",
                "pages": [
                    1
                ],
                "position": {
                    "x": 0.6,
                    "y": 0.75,
                    "width": 0.25,
                    "height": 0.08
                }
            }
        ]
    }
}
HTTP/2 201

{
    "id": "Vt7RfKq2LmXe5ZwGyB4N",
    "status": "completed",
    "completedAt": "2026-05-11T09:30:00.000Z",
    "document": {
        "id": "Dq4RfKp2LmXe5ZwGyB4N",
        "mainFileId": "b71f0c9de2a34c7f8de51a2c6b90f4e37c18a5d0429e6bb3f1c7a8d25e93b60a4",
        "metadataDefinition": "d_default",
        "signatureProvider": "internal",
        "templateId": null,
        "title": "Internal Policy Acknowledgement"
    },
    "recipients": [
        {
            "id": null,
            "recipient": "john.smith@example.com",
            "purpose": "sign",
            "status": "completed",
            "completedAt": "2026-05-11T09:30:00.000Z",
            ...
        }
    ],
    ...
}

There was nobody to wait for, so the transaction is completed the moment it is created, and you appear as its single, already finished recipient. That entry is synthetic — its id is null, because there is nobody to remind, reassign or remove — so skip it when you feed recipients into management calls. Note document.mainFileId: signing produces a new file — the signed PDF — which you download the same way as after any other transaction.

A signature is what you get without saying otherwise; to stamp your initials instead, set the signature's type and give it the fileId of one of your configured initials images.

Important

A document runs exactly one transaction, and signing it yourself uses it up. If the document also has to go to somebody else, ask for both in the same call — apply and send together sign it on your behalf and then send it on. Afterwards it is too late: a second transaction on the same document is refused with 409.

Step 3: Check the signature on the document

GET /documents/Dq4RfKp2LmXe5ZwGyB4N
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "id": "Dq4RfKp2LmXe5ZwGyB4N",
    "title": "Internal Policy Acknowledgement",
    "signatures": [
        {
            "comment": null,
            "signatureProvider": "internal",
            "signedAt": "2026-05-11T09:30:00.000Z",
            "signedBy": "john.smith@example.com",
            "type": "signature"
        }
    ],
    "transaction": {
        "id": "Vt7RfKq2LmXe5ZwGyB4N",
        "status": "completed",
        "waitingFor": []
    },
    ...
}

The signature is recorded on the document with who applied it and when, and the document's transaction shows the process behind it is completed. From here the usual evidence — the audit trail and the certificate of fulfillment — is available for this document like for any other.

Complete example

Signing a document with your own signature, 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 me = await (await fetch(`${BASE_URL}/me`, { headers: AUTH_HEADER })).json();
if (me.signatures.length === 0) throw new Error("No signature configured for this account"); // error handling kept minimal for brevity

const pdfBase64 = (await fs.readFile("internal-policy.pdf")).toString("base64");

const signRes = await fetch(`${BASE_URL}/transactions`, {
    method: "POST",
    headers: { ...AUTH_HEADER, "Content-Type": "application/json" },
    body: JSON.stringify({
        document: {
            title: "Internal Policy Acknowledgement",
            fileContentBase64: pdfBase64
        },
        apply: {
            signatures: [
                {
                    fileId: me.signatures[0].fileId,
                    pages: [
                        1
                    ],
                    position: {
                        x: 0.6,
                        y: 0.75,
                        width: 0.25,
                        height: 0.08
                    }
                }
            ]
        }
    })
});

const transaction = await signRes.json();
console.log(`Signed, transaction ${transaction.status}`);

// the signed PDF is a new file
const signed = await fetch(`${BASE_URL}/files/${transaction.document.mainFileId}/content`, { headers: AUTH_HEADER });
await fs.writeFile("internal-policy-signed.pdf", Buffer.from(await signed.arrayBuffer()));

Next steps