Skip to main content
The Teams API enables zero-knowledge multi-agent collaboration. The server processes and stores only public keys, wrapped Data Encryption Keys (DEKs), and encrypted memory ciphertext — it never holds cleartext keys or decrypts any memory content. Access control is enforced through asymmetric key wrapping: you issue the team DEK wrapped to each member’s public key, and each member independently decrypts it locally using their private key.
Session JWT required. All team endpoints require your dashboard session JWT (issued by WorkOS AuthKit, verified via remote JWKS — see Authentication) in the Authorization header. Requests using sov_live_ or sov_proj_ tokens return 401 session_jwt_required.

POST /teams

Create a new collaborative team and register the owner’s self-grant for the team’s Data Encryption Key.
string
required
A display label for the team. Between 1 and 128 characters.
string
required
The owner’s public key as a PEM-encoded string. Used for asymmetric wrapping of the team DEK.
string
required
The team’s Data Encryption Key (DEK), encrypted (wrapped) to the owner’s public key. The server stores this opaque blob and returns it to the owner when they call GET /teams/:id/grant.

Response — 201 Created

curl Example


POST /teams/:id/grants

Invite a new member or update an existing member’s access by publishing the team DEK wrapped to the recipient’s public key.
string
required
Identifier for the new member (e.g. their wallet address or user ID).
string
required
The recipient’s PEM-encoded public key. The team DEK must be wrapped to this key before submission.
string
required
The team DEK encrypted to member_pubkey. The server stores this opaque blob for the member to retrieve via GET /teams/:id/grant.
string
required
Access level for this member. One of "admin", "write", or "read".

Response — 201 Created

Issuing a grant to a new member runs the same seat-quota check as POST /teams/:id/invitations below — on a paid team plan at its seat limit, this can automatically increment your Polar subscription’s seat count.

GET /teams/:id/grant

Fetch the wrapped DEK registered for the authenticated caller. Decrypt it locally with your private key to obtain the team’s plaintext DEK.

Response — 200 OK

Returns 403 no_grant if the caller has not accepted an invitation or otherwise holds no grant for this team — this is how a revoked member is locked out: their grant row was deleted, so they cannot retrieve the DEK.

curl Example


POST /teams/:id/rotate

Revoke a member, increment the team’s rotation epoch, and re-wrap the team DEK to all remaining active members. Clients must generate a new DEK, wrap it individually to each remaining member’s public key, and submit all updated grants in a single atomic operation.
string
Identifier of the member to revoke. Optional — omit to rotate the DEK on a routine schedule without revoking anyone.
string
Human-readable reason for revocation (e.g. "offboarding", "security_incident"). Defaults to "manual_rotation" if omitted.
array
required
Array of updated grant objects for every remaining active member. Each must include member_id (string) and wrapped_dek (string) — the new DEK wrapped to that member’s existing public key.

Response — 200 OK


POST /teams/:id/memories

Append a client-encrypted ciphertext block to the team’s shared memory history. All active members holding the current DEK can decrypt this block locally. Requires write role or higher.
string
required
Base64-encoded AES-256-GCM ciphertext with the GCM auth tag appended. Maximum decoded size is 256 KB.
string
Identifier to attribute this write to in the sync log. Optional — defaults to the authenticated caller’s user ID.

Response — 201 Created


GET /teams/:id/memories

Retrieve the complete encrypted memory ciphertext history for the team. Decrypt each entry locally using the team DEK you obtained from GET /teams/:id/grant. Requires read role or higher — a revoked (inactive) member is rejected here, so even ciphertext is withheld.

Response — 200 OK


POST /teams/:id/subkeys

Issue a programmatic sov_live_ API key bound to a specific team member grant. Sub-keys inherit the member’s role and are independently revocable without affecting other keys. Requires write role or higher to bind a sub-key to yourself; binding to a different member requires admin.
string
required
A human-readable label for this sub-key (e.g. "ci-sync-script"). Between 1 and 128 characters.
string
Identifier of the team member this sub-key is bound to. Optional — defaults to the authenticated caller. The bound member must already hold a grant.

Response — 201 Created

The raw_secret is shown exactly once and is never stored in plaintext. Copy it now — you cannot retrieve it again.

GET /teams/:id/sync-log

Retrieve per-agent telemetry statistics for the team’s recent replication activity (most recent 200 entries). Requires read role or higher.

Response — 200 OK


GET /teams/:id/members

List all team members with their roles, activation statuses, and grant states. Requires read role or higher. Never returns wrapped_dek, member_pubkey, or any ciphertext — roster metadata only.

Response — 200 OK

curl Example


GET /teams

List every team the authenticated user belongs to (active memberships only).

Response — 200 OK


POST /teams/:id/invitations

Invite a new member by email. Admin-gated. Triggers a seat-quota check — if the team is at its seat limit on a paid plan, this automatically increments the Polar subscription’s seat count and emails the owner; on a plan that doesn’t support auto-scaling, it returns 403 seat_limit_reached instead and emails the owner that a seat is needed.
string
required
The invitee’s email address.
string
Access level to grant on acceptance. One of "admin", "write", "read". Defaults to "read".

Response — 201 Created


POST /teams/:id/guardians/invite

Invite a Shamir key-recovery guardian by email — a distinct invitation type from a regular team member, used for the Managed Key Recovery escrow flow.
string
required
The guardian’s email address.

Response — 201 Created


GET /teams/:id/audit

List the team’s tamper-evident audit ledger entries, newest first. Requires active membership. Supports optional actor, action, from, to query filters and limit (default 200, max 1000). Each row includes prev_hash and entry_hash so a client can re-verify the SHA-256 hash chain locally — see Trust Center for the chain construction.

Response — 200 OK

GET /teams/:id/audit/verify

Re-verify the audit chain’s hash integrity server-side and report the result.

Response — 200 OK

If the chain has been tampered with, the response reports the exact broken_seq.

GET /teams/:id/audit/export

Admin-gated (a full ledger export leaving the system is treated as an admin-grade action). Accepts optional from/to date filters. Returns the same verified, well-formed JSON export used for compliance evidence.