Spaces

Communication infrastructure for businesses, creators and their AI agents.

Hosted MCP Server

npx add-mcp 'https://api.spacesos.com/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

Quickstart

  1. Create an account with your email and the code we send.
  2. In the dashboard, connect WhatsApp (scan a QR code), or Instagram, LinkedIn or Facebook Messenger (sign in).
  3. Create an API key. It starts with spc_live_ and is shown once.
  4. Add the MCP server to your agent, or call the REST API.

MCP server: https://api.spacesos.com/mcp · REST base URL: https://api.spacesos.com/v1 · Auth: Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx

Sign up and set up from an agent

An agent can do the whole setup over the API or MCP. Only two steps need the person: reading the code from their inbox, and opening the link to connect an account. The first connect starts the Free plan. No card needed.

  1. POST /signup with the email (or the create_account tool). We email a 6-digit code.
  2. POST /signup/verify with the code (or verify_account). You get an API key.
  3. POST /messaging/accounts/connect-link (or create_messaging_connect_link) returns a link where the person scans a QR code or signs in. The first connect starts the Free plan: 1 account and 20 actions a day.
  4. To choose a plan, POST /messaging/billing/checkout (or create_messaging_billing_checkout) with plan and interval. It returns a link where the person adds a card. Do this for a second account or more than 20 actions a day.
  5. GET /messaging/accounts (or list_messaging_accounts) confirms the account is live. Keys: GET, POST and DELETE /keys.

Paste this into Claude Code, Claude Desktop or any agent that can browse and run commands.

Set me up on Spaces so you can read and send my messages. Instructions: https://www.spacesos.com/llms.txt

Connect an account and choose a plan, with a key

curl -X POST https://api.spacesos.com/v1/messaging/accounts/connect-link \
  -H "Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "whatsapp" }'
curl -X POST https://api.spacesos.com/v1/messaging/billing/checkout \
  -H "Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "success_url": "https://app.spacesos.com/dashboard", "cancel_url": "https://app.spacesos.com/dashboard" }'

Connect your agent

The MCP server gives your agent tools to list accounts and chats, read messages, reply and start chats.

Run this once in your terminal. Claude Code can then read and send your messages.

claude mcp add --transport http spaces https://api.spacesos.com/mcp --header "Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx"

One MCP server per channel

https://api.spacesos.com/mcp covers every channel. Each channel also has its own server, limited to that channel's accounts and tools: https://api.spacesos.com/mcp/whatsapp, https://api.spacesos.com/mcp/instagram, https://api.spacesos.com/mcp/linkedin, https://api.spacesos.com/mcp/messenger and https://api.spacesos.com/mcp/x. The same key and the same limits apply. An account from another channel is refused.

Claude, ChatGPT, Cursor and Grok

ClientHow it connects
Claude Codeclaude mcp add --transport http spaces https://api.spacesos.com/mcp --header "Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx"
Claude DesktopSettings, Developer, Edit Config: add the server with mcp-remote and your key, then restart.
Claude APIPass the URL in mcp_servers with your key as authorization_token.
ChatGPTA custom MCP connector with the URL and the Authorization header.
CursorAn MCP server named spaces with the URL and header, or OAuth.
GrokThe xAI API's MCP tool with the URL and header, or OAuth.
Grok BotOAuth, no key: it finds Spaces through /.well-known/oauth-authorization-server and you approve access.

Any of these URLs can be one of the per-channel servers above.

Which key permissions do I need?

New keys can read and send. A read-only key is enough for agents that only summarise or search your messages.

MCP tools

Two servers. https://api.spacesos.com/mcp/start needs no key and signs people up. https://api.spacesos.com/mcp takes Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx and does everything else. Each tool describes its own limits, so Claude and Grok can plan around them.

ToolArgumentsWhat it does
create_accountemailOnboarding server, no key. Emails the person a 6-digit code.
verify_accountsignup_id, codeOnboarding server. Returns the API key and how to connect.
list_messaging_accountsnoneConnected accounts on every channel.
list_messaging_chatsaccount_id, cursor?, limit?, unread?Chats on an account, newest first.
list_messaging_messagesaccount_id, chat_id, cursor?, limit?Messages in a chat.
get_messaging_attachmentaccount_id, chat_id, message_id, attachment_idOne attachment as base64 (up to 5 MB).
reply_in_chataccount_id, chat_id, text, idempotency_key, attachments?Reply in an existing chat. Needs a key that can send.
start_chataccount_id, to (phone, username or user_id), text, idempotency_keyStart a new chat. Subject to the new-chat limits.
create_messaging_connect_linkchannel or account_idA link the person opens to connect (QR code or login), or to reconnect.
disconnect_messaging_accountaccount_idDisconnect an account; billing for it stops.
get_messaging_billingnoneSubscription status, connected accounts and monthly total.
create_messaging_billing_checkoutplan, intervalChoose a paid plan. Returns a link where the person adds a card.
get_profileaccount_id, identifierA profile on any channel. On LinkedIn it counts as a profile view.
send_invitationaccount_id, identifier, message?, idempotency_keyLinkedIn: invite someone to connect.
list_invitationsaccount_idLinkedIn: pending invitations you sent.
cancel_invitationaccount_id, invitation_idLinkedIn: withdraw an invitation.
search_linkedinaccount_id, keywords, category?LinkedIn: search people or companies.
list_user_postsaccount_id, identifierLinkedIn: recent posts of a person or company.
get_postaccount_id, post_idLinkedIn: one post.
list_commentsaccount_id, post_idLinkedIn: comments on a post.
create_postaccount_id, text, idempotency_keyLinkedIn: publish a post.
comment_on_postaccount_id, post_id, text, comment_id?, idempotency_keyLinkedIn: comment, or reply to a comment.
react_to_postaccount_id, post_id, reaction?, idempotency_keyLinkedIn: like, celebrate, support, love, insightful or funny.

REST API

Every request needs your key. Account ids come from GET /messaging/accounts.

MethodPathWhat it does
GET/messaging/accountsYour connected accounts
GET/messaging/accounts/{account_id}/chatsChats, newest first (cursor, limit, unread)
GET/messaging/accounts/{account_id}/chats/{chat_id}/messagesMessages in a chat
POST/messaging/accounts/{account_id}/chats/{chat_id}/messagesReply in a chat
POST/messaging/accounts/{account_id}/chatsStart a chat (WhatsApp: to.phone; Instagram, X, LinkedIn: to.username; Messenger: to.user_id)
GET/messaging/accounts/{account_id}/users/{identifier}A profile (on LinkedIn, counts as a profile view)
GET/messaging/accounts/{account_id}/chats/{chat_id}/messages/{message_id}/attachments/{attachment_id}Download an attachment
POST/messaging/webhooksGet a signed POST for new messages, reads, reactions, comments, sends, posts and account status
POST/messaging/webhooks/{id}/testSend one signed example event to that webhook now
GET/messaging/accounts/{id}/healthIs this account working, and what to do if not
PATCH/messaging/accounts/{id}Tag an account with your own id (external_id)
GET/messaging/analytics/postsHow your LinkedIn and Instagram posts are doing
GET/messaging/usageSends and new chats per account against its limits
POST/messaging/accounts/{account_id}/invitationsLinkedIn: send an invitation { to, message? }
GET / DELETE/messaging/accounts/{account_id}/invitations[/{invitation_id}]LinkedIn: sent invitations, withdraw one
POST/messaging/accounts/{account_id}/searchLinkedIn: search { keywords, category: people | companies }
GET/messaging/accounts/{account_id}/users/{identifier}/postsLinkedIn: posts of a person or company
GET/messaging/accounts/{account_id}/posts/{post_id}[/comments]LinkedIn: a post, its comments
POST/messaging/accounts/{account_id}/postsLinkedIn: publish a post { text }
POST/messaging/accounts/{account_id}/posts/{post_id}/commentsLinkedIn: comment { text, comment_id? }
POST/messaging/accounts/{account_id}/posts/{post_id}/reactionsLinkedIn: react { reaction }
GET/integrations/{account_id}/tiktok/accountTikTok: account name, followers, likes and videos. Optional start_date and end_date add views and new followers
GET/integrations/{account_id}/tiktok/videosTikTok: your videos, newest first, with caption, link, views, likes and comments
POST/integrations/{account_id}/tiktok/videosTikTok: post a video from a public https link. The owner picks privacy_level and approves the exact post (owner_approved: true)
POST/integrations/{account_id}/tiktok/photosTikTok: post up to 35 photos as one post. The owner picks privacy_level and approves the exact post
GET/integrations/{account_id}/tiktok/videos/{video_id}/commentsTikTok: comments on one of your videos
POST/integrations/{account_id}/tiktok/videos/{video_id}/comments/{comment_id}/repliesTikTok: reply to a comment, once the owner approves the exact reply
GET / POST / DELETE/keysList, create and revoke keys
POST/keys/{key_id}/pause, /keys/{key_id}/resumeSwitch a key off and on again

List chats

curl "https://api.spacesos.com/v1/messaging/accounts/ACCOUNT_ID/chats?limit=20" \
  -H "Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx"

Reply in a chat

curl https://api.spacesos.com/v1/messaging/accounts/ACCOUNT_ID/chats/CHAT_ID/messages \
  -H "Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-7781" \
  -d '{ "text": "Thanks, see you at 3." }'

Start a WhatsApp chat

curl https://api.spacesos.com/v1/messaging/accounts/ACCOUNT_ID/chats \
  -H "Authorization: Bearer spc_live_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-confirmation" \
  -d '{ "to": { "phone": "+15551234567" }, "text": "Your order is ready." }'

Every send needs an Idempotency-Key (8 to 128 letters, digits, dots, dashes or colons). Retrying with the same key never sends twice. If we cannot tell whether a message went out, the send is marked UNKNOWN and is not retried for you.

Limits

Every account stays inside the limits each channel allows, enforced on our side for every key and every agent, however it is prompted. New accounts start lower and warm up over four weeks. A customer can set a lower cap. The cap cannot go above these ceilings.

ChannelBetween messagesNew chatsMessages per dayFirst week
WhatsAppA random 10 to 20 seconds, and 1 to 2 minutes before a new chat5 an hour, 10 a day400No new chats for 24 hours, then 1 an hour and 3 a day
Instagram15 seconds10 an hour, 100 a day, shared with likes, comments, follows and views1504 an hour, 40 a day
LinkedIn20 seconds15 an hour, 100 a day10040 new chats and up to 40 invitations a day
Messenger20 seconds3 an hour, 8 a day501 an hour, 3 a day
X20 seconds5 an hour, 20 a day1002 an hour, 6 a day

What each channel can do

ChannelInbox and historyDownload attachmentsSend with attachmentsNotes
WhatsAppYesYesYesNew chat uses a phone number
InstagramYesYesYesNew chat uses a username. Prefer a reply after the person has written.
MessengerYesYesYesNew chat uses the user id from an existing chat
LinkedInYesYesYesA normal message goes to a 1st-degree connection. Set inmail: true for an InMail. A connection request with a note is send_invitation.
XYesYesYesNew chat uses a username

On LinkedIn and Instagram you can list your posts, read comments and the replies under a comment, reply, react, and delete a comment you wrote. Instagram can also send the commenter a direct message. Comment replies have their own caps: 15 an hour and 100 a day on LinkedIn, and on Instagram they share the 10 an hour and 100 a day with likes, follows and new conversations. A direct message still uses the message caps. A routine can watch for a new comment or a keyword and reply, including from the snippets saved on that routine, and optionally send the direct message too. New comments are delivered as comment.received.

ChannelYour postsRead and replyReactDelete yoursHideDirect message the commenter
LinkedInYesYesYesYesNoNo
InstagramYesYesLikeYesNoYes
MessengerNoNoNoNoNoNo. Page comments are not available on a Messenger account.
XNoNoNoNoNoReplies and mentions are not available here.
WhatsAppNoNoNoNoNoWhatsApp has no post comments.

Schedules and routines

Schedule a message for a local time, or save a routine. A routine can run every day, on a cron, on an interval, or when something happens: a new message, a keyword, an accepted connection, or no reply after a number of hours. The action can send a message, reply from snippets you saved on the routine, tag the chat, call your webhook, or email a short unread summary. You can pause, resume, or delete a routine and read its runs, including errors. Every one of those sends uses the same limits as a message you send by hand. A limit delays the job. A paused or restricted account stops it. This reduces the chance of a restriction. It cannot guarantee the platform will never restrict the account.

Keeping your accounts safe

Spaces reduces ban risk with enforced limits, warm-up, pacing and an automatic pause. It cannot guarantee the platform will not restrict the account. These limits apply to the API and to MCP. A 429 means nothing was sent. Wait retry_after_seconds, then retry with the same idempotency key. A send includes account_safety (remaining today, remaining this week, whether sending is paused, and why). New chats on a fresh account ramp up over four weeks. The same cold text cannot be sent to many people. If someone says stop, later sends to that chat are refused. If the account needs a checkpoint, new credentials, or is restricted, sending pauses until it is reconnected. You can read current usage with GET /messaging/usage.

LinkedIn actions

ActionLimit per account
InvitationsFree account: 100 a day and 150 a week. Premium, Sales Navigator or Recruiter: 100 a day and 200 a week. Spaced at random during working hours (22 a day on days 1 to 3, then 40, 60 and 85)
InMail30 a day on a free account, 50 on Premium, Sales Navigator or Recruiter (about 800 a month)
Profile views20 an hour, 100 a day (150 on Sales Navigator, 200 on Recruiter)
Searches20 an hour, 100 a day (about a thousand results, or 2,500 on Sales Navigator and Recruiter)
Reading posts, comments and invitations30 an hour, 100 a day
Comments15 an hour, 100 a day
Reactions20 an hour, 100 a day
Posts100 a day, the same as other actions

Profile lookups on other channels: up to 100 a day on WhatsApp and Instagram (on Instagram inside its shared 100 actions), 40 on Messenger and 50 on X.

Every action warms up over four weeks on a new or reconnected account, and a reply to someone who wrote in is never held back by the daily limits. Over a limit you get 429 with a Retry-After header and retry_after_seconds in the body; nothing was sent. Agents should wait that long and try again. See your live numbers with GET /messaging/usage or on the dashboard.

Errors

StatusMeaning
401Missing, wrong or revoked key
403The key lacks the permission for this call; KEY_SUSPENDED means the key is paused, ACCOUNT_SUSPENDED means the workspace is suspended
404Account, chat or recipient not found
409Idempotency key reused for a different message, or a send in progress
429Over a limit; wait retry_after_seconds (also in Retry-After)
502, 503The channel is unavailable right now; nothing was sent, retry later

Every refusal carries code, error and doc_url, a page that explains the code and what to do. The common ones are on the limits page. To check one account, call GET /v1/messaging/accounts/{account_id}/health. Spotted a bug or a gap? POST /v1/feedback reaches the team. What changed: changelog.