Identity callables
Base URL:
https://us-central1-axy-ujex.cloudfunctions.net
All functions use Firebase callable JSON. Human-owner calls require a Firebase
Auth ID token. Agent calls use the session device-key exchange, then
Authorization: Bearer <firebase-id-token>.
| Function | Caller | Purpose |
|---|---|---|
session | unauthenticated body with agentId + deviceKey | Mint a Firebase custom token for an agent device key. |
createAgent | human owner | Create an agent and claim a mailbox local part. Mailbox provisioning is deferred — see below. |
identityProvisionMailbox | human owner | Retry mailbox provisioning for an owned agent, on demand. |
deleteAgent | human owner | Suspend/offboard an owned agent, retire its mailbox, and preserve owner/audit evidence. |
rotateDeviceKey | human owner | Mint a new device key and optionally revoke existing keys. |
identityListDeviceKeys | human owner | List key prefixes and metadata. |
revokeDeviceKey | human owner | Revoke a device key by prefix. |
delegate | agent | Mint a short-lived delegation token for another owned agent. |
identityListMandates | human owner or permitted team member | Read current and historical mandate versions. |
identityGrantMandate | human owner or permitted team member | Grant authority when no active mandate exists. |
identityAmendMandate | human owner or permitted team member | Replace scope/term using optimistic version checking. |
identityRevokeMandate | human owner or permitted team member | Revoke current authority and invalidate sessions. |
storeSecret | human owner or authorized agent | Store an encrypted agent secret. |
listSecrets | human owner | List secret names. |
deleteSecret | human owner | Delete a secret. |
getSecret | authorized agent | Retrieve a scoped secret value. Requires sessionId and stamps sensitive_access=true on that session. |
See https://ujex.dev/openapi.json for the
machine-readable Inbox/session subset.
All protected agent actions require the credential and its time-bounded mandate to remain current. Revocation, expiry, suspension, or a stale session version fails closed.
Agent creation and mailbox provisioning
Creating an agent and provisioning its Ruzayo mailbox are separate steps.
createAgent persists the agent, its mailbox claim and its device key first,
then attempts provisioning. If Ruzayo is unreachable the agent is still created
and the mailbox is provisioned later; the agent is never deleted because mail
provisioning failed.
createAgent returns three additional fields (added alongside the existing
agentId / deviceKey / prefix / localPart / inboundDomain, which are
unchanged):
| Field | Type | Meaning |
|---|---|---|
mailState | "pending" | "provisioned" | "unavailable" | Provisioning truth for the mailbox. |
mailStateReason | string | null | Why, when not provisioned (e.g. provider_unreachable, address_taken). |
mailboxReady | boolean | true only when mailState === "provisioned". |
pending— Ruzayo was unreachable, returned a transient error, or answered without confirming the mailbox. A durable retry job is queued and drained by theidentityRetryMailProvisioningscheduled function every 5 minutes with exponential backoff, for roughly 9.4 days. Mail cannot be sent or received from this address yet, andpostboxSendrejects withfailed-preconditionrather than accepting a message that cannot leave.provisioned— Ruzayo confirmed the mailbox: it returned the address that was requested, markedenabled. This is the only state in which mail works, the only state in which the mailbox document isenabled, and the only state in which a signed agent passport asserts the mail address at all.unavailable— terminal for this address. Either Ruzayo rejected it (address_taken,address_invalid), provisioned a different address (address_mismatch), the deployment is misconfigured (provider_misconfigured), or transient retries were exhausted (retry_exhausted:*). Nothing retries automatically; the owner must act.
A pending mailbox always has convergence scheduled. The retry job is written
before the provider call, and identityReconcileMailProvisioning re-queues any
mailbox left pending with no live job. mailboxReady: false never means "and
nothing is happening".
The same three fields appear on each row of getAgentDetails, and
postboxListInboxes reports mailState per inbox. Mailboxes created before
provisioning was deferred carry no mailState; readers treat that absence as
provisioned, because those mailboxes were provisioned inline.
identityProvisionMailbox
{ "agentId": "<agentId>", "mailboxId": "default" }
Returns {agentId, mailboxId, mailState, mailStateReason, mailboxReady, retryable}.
It is idempotent: an already-provisioned mailbox short-circuits without calling
Ruzayo, and the underlying provisioning call is an upsert keyed on the address.
An unavailable mailbox may be retried through this endpoint once the owner has
resolved the conflict.
The retry clears the backoff, not the attempt budget, and runs through the same
lease the scheduled drain uses. So it performs exactly one real attempt, it
cannot run concurrently with a drain already calling Ruzayo for that mailbox,
and repeated retries on a mailbox whose budget is spent report unavailable
rather than holding it open indefinitely.