Provision and manage members

Your integration can open accounts for the people who join your organization, adjust what they are allowed to do, and close those accounts down again. This guide follows one member through that whole life, in five calls.

Before you begin

  • An organization-administrator key. Reading members only takes the permission to view users, but creating, changing and deleting them is reserved for an organization administrator.

Step 1: Create a member

A new member needs an email address and a name — nothing else:

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

{
    "email": "emma.wilson@example.com",
    "fullname": "Emma Wilson"
}
HTTP/2 201

{
    "company": null,
    "dateFormat": "dd/MM/yyyy",
    "email": "emma.wilson@example.com",
    "firstActivityAt": null,
    "fullname": "Emma Wilson",
    "groupIds": [
        "7e52b9c1-8f40-4a6d-b3e9-1c27d5a08f63",
        "c4a1f0d2-6b3e-4f28-9a71-5d08e3b7c214"
    ],
    "id": "emma.wilson@example.com",
    "jobTitle": null,
    "language": "en",
    "organizationId": "c4a1f0d2-6b3e-4f28-9a71-5d08e3b7c214",
    "phone": null,
    "registeredAt": "2026-05-11T09:32:00.000Z",
    "role": "member",
    "status": "newly_created",
    "statusChangedAt": "2026-05-11T09:32:00.000Z",
    "timeFormat": "h:mm a",
    "timezone": "UTC"
}

Two things were decided for you, and both can be overridden. The account is newly_created, which sends an invitation email asking the person to set their own password. And the role is member, the ordinary organization member with access to documents and signing.

How many members your organization may have is a matter of its subscription, and a creation that would go over that limit is refused with 402.

The id is what addresses this member from now on. It is normally the address you supplied, but an address that once belonged to a since-deleted account yields a different identifier, so keep the id you were given instead of rebuilding it from the email.

Tip

To hand out credentials yourself instead of inviting the person, create the account with status set to active and a password of your choosing. The account is then usable immediately, and communicating the password is up to you.

Step 2: List the members of your organization

The collection only ever contains your own organization:

GET /users
Authorization: Bearer YOUR_API_KEY
HTTP/2 200

{
    "total": 3,
    "pagination": {
        "limit": 20,
        "nextCursor": null,
        "offset": 0
    },
    "results": [
        {
            "id": "emma.wilson@example.com",
            "fullname": "Emma Wilson",
            "jobTitle": null,
            "role": "member",
            "status": "newly_created",
            ...
        },
        {
            "id": "jane.doe@example.com",
            "fullname": "Jane Doe",
            "jobTitle": "HR Specialist",
            "role": "member",
            "status": "active",
            ...
        },
        {
            "id": "john.smith@example.com",
            "fullname": "John Smith",
            "jobTitle": "Head of Operations",
            "role": "admin",
            "status": "active",
            ...
        }
    ]
}

Narrow it down with the query parameters the API reference lists: by account status, by role, by the permissions a member holds, by group, or with a free-text search over names and addresses. Every filter you add must match.

Step 3: Change what a member may do

A PATCH changes only the fields you send:

PATCH /users/emma.wilson@example.com
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
    "role": "admin"
}
HTTP/2 200

{
    "id": "emma.wilson@example.com",
    "fullname": "Emma Wilson",
    "groupIds": [
        "c4a1f0d2-6b3e-4f28-9a71-5d08e3b7c214",
        "2d90f4a7-15c8-4b3e-8f61-9a04c7e2b5d1"
    ],
    "role": "admin",
    "status": "newly_created",
    ...
}

The new profile replaces the previous one — groupIds shows the administrator group in place of the member group, with the organization untouched. A profile is always set here, on the member; the groups you manage yourself are added and removed separately, in Manage group membership.

Step 4: Suspend a member

Suspension shuts an account without removing it:

PATCH /users/emma.wilson@example.com
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
    "status": "suspended"
}
HTTP/2 200

{
    "id": "emma.wilson@example.com",
    "role": "admin",
    "status": "suspended",
    "statusChangedAt": "2026-05-11T09:34:00.000Z",
    ...
}

The account cannot be used until it is reactivated, and statusChangedAt records the moment it was shut. Nothing else about the member changes, and setting the status back to active lets them straight back in — which makes suspension the right answer while you are still deciding.

Step 5: Delete a member

Deleting revokes access for good:

DELETE /users/emma.wilson@example.com?reassign=jane.doe@example.com
Authorization: Bearer YOUR_API_KEY
HTTP/2 204

The reassign parameter names the member who inherits the leaver's documents. It is worth supplying whenever the person did any work in Circularo.

Warning

Without reassign the member's own documents, drafts, templates and private files are deleted permanently and their pending transactions are cancelled. Nothing about a deletion can be undone.

Complete example

Provisioning a member and giving them the standing your own system says they should have, as a Node.js script. Error handling is minimal for brevity — in production, inspect the error envelope of non-2xx responses.

JavaScript
const BASE_URL = "https://sandbox.circularo.com/api/v1/public"; // your instance's base URL
const API_KEY = "YOUR_API_KEY";
const HEADERS = { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" };

const res = await fetch(`${BASE_URL}/users`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
        email: "emma.wilson@example.com",
        fullname: "Emma Wilson"
    })
});
if (!res.ok) throw new Error(`Provisioning failed: ${res.status}`); // error handling kept minimal for brevity

const member = await res.json();
// store this identifier against your own record of the person
console.log(`${member.fullname} is now ${member.id}`);

await fetch(`${BASE_URL}/users/${member.id}`, {
    method: "PATCH",
    headers: HEADERS,
    body: JSON.stringify({
        role: "admin"
    })
});

Next steps