Spaces
Docs / Partners

Spaces for agent companies.

If you build an AI agent product, add Spaces once. Each of your users then connects their own WhatsApp, Instagram, LinkedIn, Messenger, X or email inside your app, and your agent uses every Spaces tool for them.

Partner accounts are in early access. To become a partner, email hello@spacesos.com with your workspace and the domains your app runs on. Your users never sign up on spacesos.com and are never asked for a card: you are the customer.

How it works

  1. Sign up on spacesos.com as usual. We make your workspace a partner and send you a partner key. It starts with spc_live_.
  2. When one of your users turns on messaging, create a Spaces user for them with your own id for that user.
  3. Create a key for that user. Your agent uses it with the REST API or the MCP server, exactly as in the main docs.
  4. Ask Spaces for a connect link and send your user to it. They see your name and logo, agree, and sign in on the platform. Then they come back to your app.
  5. Set caps or pause a user whenever you need to. Read usage each month. Get every user's events on one webhook.

Two kinds of key

Your partner key manages your users. It works only on https://api.spacesos.com/v1/partner and cannot read or send messages.

A user key belongs to one of your users. It can read and send for that user and nobody else. Every tool, route and limit in Spaces works with it as it does for any customer. A user key cannot connect accounts, create keys or change billing, so those steps always go through you.

We chose one key per user over a single key with a user header. A key that only knows one user cannot reach another user's messages by mistake, a leaked key exposes one user only, and you can revoke one user's access without touching the rest. Store each user key next to that user in your database.

Users

curl -X POST https://api.spacesos.com/v1/partner/users \
  -H "Authorization: Bearer $PARTNER_KEY" -H "content-type: application/json" \
  -d '{"external_user_id": "user_123", "name": "Dana"}'

Calling it again with the same external_user_id returns the same user, so it is safe to call each time a user opens your settings. Email and name are optional.

  • GET /partner/users: your users, newest first (limit, before, external_user_id).
  • GET /partner/users/{id}: one user with their connected accounts, caps and this month's usage.
  • PATCH /partner/users/{id}: change email, name, caps or paused.
  • POST /partner/users/{id}/keys: a user key, shown once. Scopes: read, messages:send, messages:draft.
  • GET /partner/users/{id}/keys and DELETE /partner/users/{id}/keys/{key_id}.
  • DELETE /partner/users/{id}/accounts/{account_id}: disconnect one account.
curl -X POST https://api.spacesos.com/v1/partner/users/$SPACES_USER_ID/keys \
  -H "Authorization: Bearer $PARTNER_KEY" -H "content-type: application/json" \
  -d '{"scopes": ["read", "messages:send"]}'

Connecting accounts

curl -X POST https://api.spacesos.com/v1/partner/users/$SPACES_USER_ID/connect-links \
  -H "Authorization: Bearer $PARTNER_KEY" -H "content-type: application/json" \
  -d '{"channel": "whatsapp", "return_url": "https://app.example.com/settings/accounts"}'

You get a url on app.spacesos.com that works for an hour. The page shows your name and logo and tells your user, in plain words, that you use Spaces to connect their account and that your AI agent will read and send messages through it. Then they sign in on the platform (WhatsApp shows a QR code).

Afterwards the browser goes to your return_url with spaces_connect=connected or spaces_connect=failed and spaces_user_id added. The return_url must be https on one of the domains we allowlisted for you. To sign an account in again, pass its account_id. For email, pass provider: gmail, outlook or imap.

Caps and pausing

Spaces already paces every account to keep it safe. You can set lower caps for each user, never higher:

  • max_accounts: connected accounts at once.
  • max_sends_per_day and max_new_chats_per_day: for each account, over a rolling day. A cap above a channel's own limit has no effect on that channel. Reading the user shows the result for each channel in effective_caps.per_channel.
  • max_actions_per_month: billable actions in the calendar month (UTC).
curl -X PATCH https://api.spacesos.com/v1/partner/users/$SPACES_USER_ID \
  -H "Authorization: Bearer $PARTNER_KEY" -H "content-type: application/json" \
  -d '{"caps": {"max_accounts": 2, "max_sends_per_day": 50, "max_actions_per_month": 2000}}'

Send null for a cap to remove it. Pause a user with POST /partner/users/{id}/pause and an optional reason; their key then answers 403 USER_PAUSED until you call /resume. Nothing is sent while a user is paused.

Events and webhooks

One webhook gets the events of all your users. Each event carries external_user_id and spaces_user_id, and is signed and retried like every Spaces webhook.

curl -X POST https://api.spacesos.com/v1/partner/webhooks \
  -H "Authorization: Bearer $PARTNER_KEY" -H "content-type: application/json" \
  -d '{"url": "https://app.example.com/hooks/spaces", "events": ["message.received", "account.status"]}'
{
  "id": "evt_...",
  "type": "message.received",
  "data": {
    "account_id": "...",
    "text": "Hi, is this still available?",
    "external_user_id": "user_123",
    "spaces_user_id": "..."
  }
}

Also GET /partner/webhooks, DELETE /partner/webhooks/{id}, POST /partner/webhooks/{id}/test and the delivery log. The same event id can arrive twice; skip repeats.

Prefer pulling? GET /partner/events is one event stream for all your users, in the same shape as GET /events: wait up to 50 seconds with wait, then continue with after=next_cursor. Every event carries external_user_id. To have that stream pushed instead, register POST /partner/events/webhooks. Each user key also reads its own user's stream with GET /events or the wait_for_events tool.

Usage

GET /partner/usage?month=2026-11 lists, for each user, the accounts they had connected during the month and the billable actions their agent took, with totals. Users you deleted still appear for the months they were active.

Partner pricing: $3.50 per connected end-user account a month plus $3 per 1,000 actions, with a $99 monthly minimum, billed monthly from /v1/partner/usage. Its invoice_preview shows what the month comes to so far.

Deleting a user

DELETE /partner/users/{id}?confirm=true disconnects every account of that user and removes their messages, keys and settings for good. Only their usage totals are kept, so the month still adds up. You can create a user with the same external_user_id again later.

Errors

  • 400 INVALID_INPUT: a field is wrong, for example a return_url outside your domains or a cap above Spaces' limit.
  • 402 PLAN_LIMIT_REACHED: a cap you set was reached.
  • 403 USER_PAUSED: you paused this user.
  • 403 PARTNER_SUSPENDED: your partner account is paused. Email hello@spacesos.com.
  • 404 NOT_FOUND: no such user for your partner key.
  • 429: Spaces is pacing the account. Nothing was sent; wait retry_after_seconds.