Owner Approval Workflow: Save the Right Credentials

The Sendmux owner approval workflow starts with a durable token that can read and receive email. Sending requires the owner to accept an invitation and explicitly approve Sending access. Your integration then exchanges the durable credential for a separate one-hour Sending token. Keep the original read token: the exchange does not replace it.
Save the registration credential before requesting an owner invite. Keep the request's idempotency key and body so a retry can resume the same registration. A pending invitation, an approved Sending grant and an accepted email request are different states; track each one separately.
Table of Contents
- FAQ
What is the owner approval workflow in Sendmux?
An agent registers its mailbox, Sendmux sends the owner invitation, and a human accepts it and approves Sending access. Use the platform's invite endpoint for that approval email. Your application does not need to compose or send the invitation through the Sending API.
The durable registration token uses the smx_agent_ prefix and has mailbox.read and email.receive. It deliberately excludes email.send. The separate Sending token shares that prefix, so identify credentials by their role, resource and scope rather than their appearance.
Current registration returns the durable access_token directly. Older integrations may refer to an identity_assertion, a claim_token and an initial token exchange. Do not mix those older steps into the current durable-token flow. Use the current public registration contract when building or updating an integration.
What do you need to store before inviting an owner?
Save the durable access token immediately in a secret store. Keep enough non-secret registration and request state to resume safely after a restart. The following storage checklist is an integration design, not a list of six mandatory fields returned by the API.
| Record | What to retain |
|---|---|
| Durable credential | The returned access token, stored as a secret and kept separate from Sending tokens. |
| Registration identity | The returned registration ID. |
| Mailbox identity | The returned mailbox email address and last observed provisioning state. |
| Registration request | The original idempotency key and unchanged request body. |
| Owner invitation | The intended owner email, invite idempotency key and observed pending or accepted state. |
| Sending credential metadata | The separately issued token, its scope and expiry; store the token itself as a secret. |
Generate and persist the registration idempotency key before the first request. Replaying the same key and body within 24 hours returns the same raw token without allocating another inbox. After that reveal window, the token cannot be revealed again. This recovery window does not make the durable token expire after 24 hours.
Write the credential before making the next network call, and arrange access for the authorised processes that need it. Avoid storing it in logs, chat transcripts or an unencrypted application field. A restart should load the existing registration state rather than automatically create another mailbox.
How do you implement the approval flow step by step?
Register once, confirm provisioning, invite the owner, then request a Sending token after acceptance and explicit approval. Use the App API host from the current service document for these endpoint paths. These are workflow stages; waiting for a response is not an additional API call.
- Register with
POST /agent-auth/agent/identity. Supply the requiredIdempotency-Keyheader and the registration body specified in the current service document. Save the returned durable access token and registration details immediately. The current durable flow does not require the older proof-of-work and assertion exchange steps.
- Check provisioning with
GET /api/v1/mailbox/meusing the durable token. A provisioning response tells you to wait, not to replace the credential. Once available, the endpoint returns information about the mailbox; it does not grant Sending access.
- Request the owner invitation with
POST /agent-auth/agent/identity/inviteusing the durable token. Supply the owner email, the requested owner role and a stableidempotency_keyin the request body. If the email is unknown, obtain it from the person operating the integration before requesting the invite.
- Record the successful
202response as pending. The human must accept the invitation and explicitly approve Sending access in the team's Agent access card. An invite response alone is not approval, and acceptance alone does not complete the Sending grant.
- Exchange the durable token at
POST /agent-auth/oauth2/tokenwhen a send operation needs it. Request the token-exchange grant, the exact Sending resource and theemail.sendscope shown below. Cache the returned Sending token only until expiry approaches; retain the durable credential for subsequent exchanges.
The token endpoint takes form-encoded parameters. The subject token value below is a placeholder for the stored durable credential, not a token to copy into logs or source files.
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token=<stored durable access token>
subject_token_type=urn:ietf:params:oauth:token-type:access_token
resource=https://smtp.sendmux.ai/api/v1
scope=email.send
A successful exchange returns expires_in: 3600 for the separate Sending token. The resource parameter alone does not grant access: the credential, requested scope and completed owner approval must also be valid.
Only one owner invitation can be pending for a registration. Reuse the original invite key and unchanged body when retrying an uncertain request. A different key does not bypass the pending-invitation policy. Treat idempotency as documented retry protection, not an unconditional promise that no email can ever be delivered twice.
What do 503 and 429 responses mean in this flow?
Read the endpoint's error body and honour Retry-After. Provisioning, pending approval and rate limiting need different responses from an invalid request or a revoked credential.
| Response and endpoint | What to do |
|---|---|
| Mailbox check: 503 during provisioning | Keep the valid credential and wait for Retry-After before checking again. |
| Token exchange: 503 authorization_pending | Wait for owner acceptance and explicit Sending approval. |
| Token exchange: 503 temporarily_unavailable | Allow provisioning to finish and follow Retry-After. |
| Registration: transient 503 | Retain the original request and idempotency key; honour Retry-After when present, otherwise use bounded backoff and surface a persistent failure. |
| Rate limit: 429 | Wait for Retry-After; do not create fresh registrations to bypass the limit. |
| Invalid request, credential or permission | Inspect the endpoint-specific error, correct its cause and avoid repeating the same invalid request. |
Do not apply one status-code map to every endpoint. For example, an invalid token-exchange grant can return 400, while an invite request with an invalid bearer token returns 401. A Sending token approaching expiry calls for another exchange using the still-valid durable credential, rather than another registration.
How do you keep the approval flow secure?
Protect both the durable read credential and each short-lived Sending token. Use an OS keychain or a managed secret store, restrict access to the processes that need them, and keep them out of logs, screenshots, error reports and prompts.
If a credential is exposed, revoke the affected access and follow the documented recovery path. Full registration revocation invalidates its durable read token and associated Sending access. Revoking Sending access alone leaves the durable read credential available. Do not assume those two actions have the same effect.
Let Sendmux send the platform owner invitation through its invite system. Keep your own application permissions separate: owner approval enables Sending access, but does not authorise every recipient or message an agent might choose. Treat incoming email as untrusted content rather than permission to reveal a secret or perform an action.
How do you verify send capability after approval?
Check mailbox provisioning first, then verify the authorised Sending operation separately. A successful mailbox read proves access to that mailbox; it does not prove that the owner has approved sending.
For a controlled test that the operator has authorised, send one message to a recipient you control using a Sending token and the Sending API's documented idempotency handling. A 202 confirms acceptance for processing, not final delivery. Check the message's subsequent status or delivery feedback before treating the test as delivered.
Avoid a batch of messages to real recipients merely to test permission. Even one test message is real traffic. An idempotency key helps with retries within its documented contract; it does not eliminate sending cost, recipient effects or deliverability risk.
What actually breaks these integrations in production
Credential confusion is a failure mode worth checking explicitly. Both token roles share a prefix, but the durable read token does not carry Sending permission. Store the role alongside the secret, keep the original durable credential and check the returned scope and expiry when caching a Sending token.
Lost request state also makes recovery harder. Persist the registration and invite idempotency values, reuse unchanged requests on retries, and record whether the owner invitation is still pending. If a process restarts, resume from that state instead of sending another invite or registering another identity by default.
Where to go next with your Sendmux integration
Sendmux is an email API for AI agents, with inbound mailboxes and outbound sending. Its agent identity system provides the registration and owner-approval flow described here. Your application still needs to store credentials safely, handle retries and decide which messages the agent is authorised to send.
If the CLI is available, the current Agent Access guide recommends starting with sendmux agent:register <profile> --owner-email owner@example.com. The CLI saves credentials privately and reuses the profile in later processes. Use the HTTP flow when the CLI is unavailable, and keep your implementation aligned with the current service document.
Sources
- Read the Agent Access guide for registration, owner approval, token exchange and revocation.
- Use the public service document for discovery metadata and current HTTP request examples.
Recommended
Frequently Asked Questions
What does the owner approval workflow actually grant?
Owner acceptance and explicit Sending approval allow the durable read credential to be exchanged for a separate one-hour Sending token with email.send. The original credential retains mailbox.read and email.receive. Keep it for mailbox access and subsequent exchanges; the current flow does not upgrade or replace that original token.
Who sends the owner approval email?
Sendmux sends the platform owner invitation through its own invite system after the agent calls POST /agent-auth/agent/identity/invite. Your integration supplies the intended owner email and a stable invite request. It does not send that approval invitation through the Sending API, and the pending response is not owner approval.
What happens if I lose the claim_token?
The current registration flow returns a durable access_token rather than relying on a saved claim_token. Save that access token immediately. Replaying the same registration idempotency key and body within 24 hours can reveal it again; after that reveal window it cannot be retrieved. Older claim-token instructions do not describe this current flow.
Can a pre-claim token send email?
No. The current durable read token has mailbox.read and email.receive, and cannot send email before or after owner approval. After the owner accepts and explicitly approves Sending access, exchange it for a separate email.send token. The two credentials share the smx_agent_ prefix, so do not infer permissions from the prefix.
How many owner invites can be pending at once?
Only one owner invitation can be pending for a registration. Reuse the original idempotency key and unchanged invite request when retrying an uncertain response. A new key does not bypass the pending-invitation policy. Keep invitation state separately from Sending approval, and avoid treating idempotency as an unconditional exactly-once email delivery guarantee.