Saperly
MESSAGING · MESSAGING
Phone numbers, the AI voice connections they run on, and SMS in that account.
Acts as the person, not as itself
Each user connects their own account. Every call carries both identities — the agent and the person it is acting for — so the agent can never reach past what that individual can already do.
Credentials never touch the agent
Tokens live in the vault and attach server-side at call time. The agent holds a session, not a secret, and revoking access does not mean rotating a key.
Every call on the record
Who asked, which agent acted, which action ran, and the verdict that let it through — one audit trail across every integration, not one per vendor.
What an agent can do
Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.
saperly_delete_connections_by_idWRITEDelete a connection. (DELETE https://api.saperly.com/connections/{id})
saperly_get_callsREADList calls. (GET https://api.saperly.com/calls)
saperly_get_calls_by_idREADGet a call by id. (GET https://api.saperly.com/calls/{id})
saperly_get_calls_by_id_recordingREADGet a call recording (302 redirect to a download URL). Saperly answers this with a 302 to a time-limited download URL rather than with audio. This integration does not stream the media through the gateway: it follows the redirect and returns a JSON descriptor naming the resolved `url`, `contentType` and `contentLength`, so fetch the recording from that URL. (GET https://api.saperly.com/calls/{id}/recording)
saperly_get_calls_by_id_transcriptREADGet a call transcript. (GET https://api.saperly.com/calls/{id}/transcript)
saperly_get_connectionsREADList all connections in the workspace. (GET https://api.saperly.com/connections)
saperly_get_connections_by_idREADGet a connection by id. (GET https://api.saperly.com/connections/{id})
saperly_get_consentREADList all consent records in the workspace. (GET https://api.saperly.com/consent)
saperly_get_consent_checkREADCheck whether a contact has active consent. (GET https://api.saperly.com/consent/check)
saperly_get_healthREADLiveness probe. NEEDS NO CREDENTIAL: measured 2026-09-16, this route answers 200 {"status":"ok"} with no Authorization header at all. It proves the Saperly API is reachable, NOT that this connection's key is valid -- call any other tool for that. (GET https://api.saperly.com/health)
saperly_get_languagesREADList the supported spoken languages. (GET https://api.saperly.com/languages)
saperly_get_messagesREADList messages (optionally filtered by number). (GET https://api.saperly.com/messages)
saperly_get_numbersREADList all phone numbers in the workspace. (GET https://api.saperly.com/numbers)
saperly_get_numbers_by_idREADGet a phone number by id. (GET https://api.saperly.com/numbers/{id})
saperly_get_pricing_quoteREADQuote the price of a phone number. (GET https://api.saperly.com/pricing/quote)
saperly_get_usageREADGet a usage summary (optionally since a timestamp). (GET https://api.saperly.com/usage)
saperly_get_voicesREADList available voices (optionally filtered by language). (GET https://api.saperly.com/voices)
saperly_get_workspaces_by_slug_audit_eventsREADList the workspace audit-log events. `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". (GET https://api.saperly.com/workspaces/{slug}/audit-events)
saperly_get_workspaces_by_slug_membersREADList the workspace members. `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". (GET https://api.saperly.com/workspaces/{slug}/members)
saperly_get_workspaces_by_slug_webhooksREADList the workspace webhook endpoints. Returns every webhook endpoint for the workspace, each including its full `signingSecret` (`whsec_…`) — the reveal surface, so the secret can be re-copied at any time to verify delivery signatures. `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". SIGNING SECRETS ARE REDACTED: each endpoint's `signingSecret` is replaced with a placeholder by this integration before the response reaches you -- a webhook signing secret can forge deliveries your system would accept as genuine, and a tool answer is replayed into transcripts and logs. Every other field is the provider's own. Read the real secret in Saperly: Settings > Webhooks. (GET https://api.saperly.com/workspaces/{slug}/webhooks)
saperly_get_workspaces_by_slug_webhooks_by_webhookid_deliveriesREADList a webhook endpoint's recent delivery attempts. Returns the endpoint's most recent delivery attempts (newest first), each with the event type, outcome (`delivered`/`failed`), HTTP `responseStatus`, attempt number, and timestamps — so you can see exactly why a delivery did or didn't land (e.g. a run of `failed` with `responseStatus: 530` means your receiver is unreachable). `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". (GET https://api.saperly.com/workspaces/{slug}/webhooks/{webhookId}/deliveries)
saperly_patch_connections_by_idWRITEUpdate a connection (re-syncs the carrier). (PATCH https://api.saperly.com/connections/{id})
saperly_post_api_tokensWRITEMint a scoped API key. (POST https://api.saperly.com/api-tokens)
saperly_post_api_tokens_by_id_revokeWRITERevoke an API key. (POST https://api.saperly.com/api-tokens/{id}/revoke)
saperly_post_assistant_answerWRITEAsk the workspace assistant a question. (POST https://api.saperly.com/assistant/answer)
saperly_post_callsWRITEPlace an outbound call. (POST https://api.saperly.com/calls)
saperly_post_calls_by_id_endWRITEEnd a live call. (POST https://api.saperly.com/calls/{id}/end)
saperly_post_calls_by_id_transferWRITEBlind-transfer a live call. (POST https://api.saperly.com/calls/{id}/transfer)
saperly_post_connectionsWRITECreate a connection (provisions inline). (POST https://api.saperly.com/connections)
saperly_post_consentWRITERecord consent for a contact. (POST https://api.saperly.com/consent)
saperly_post_consent_revokeWRITERevoke consent for a contact. (POST https://api.saperly.com/consent/revoke)
saperly_post_messagesWRITESend an SMS message. (POST https://api.saperly.com/messages)
saperly_post_numbersWRITEProvision a phone number. (POST https://api.saperly.com/numbers)
saperly_post_numbers_by_id_caller_idWRITESet the outbound caller ID name (CNAM). (POST https://api.saperly.com/numbers/{id}/caller-id)
saperly_post_numbers_by_id_connectionWRITEBind a connection to a number. (POST https://api.saperly.com/numbers/{id}/connection)
saperly_post_numbers_by_id_releaseWRITERelease a phone number. (POST https://api.saperly.com/numbers/{id}/release)
saperly_post_numbers_by_id_sms_senderWRITESet the alphanumeric SMS sender id. (POST https://api.saperly.com/numbers/{id}/sms-sender)
saperly_post_numbers_by_id_webhookWRITESet a number's manual-mode brain URL. Sets the per-number manual-mode conversation-brain URL (a fallback for the connection's `manualWebhookUrl`). NOT an event-notification webhook — to receive call/SMS events, register a workspace webhook endpoint via `POST /workspaces/:slug/webhooks`. (POST https://api.saperly.com/numbers/{id}/webhook)
saperly_post_workspaces_by_slug_api_tokensWRITECreate a scoped API token. `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". (POST https://api.saperly.com/workspaces/{slug}/api-tokens)
saperly_post_workspaces_by_slug_api_tokens_by_tokenid_revokeWRITERevoke an API token. `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". (POST https://api.saperly.com/workspaces/{slug}/api-tokens/{tokenId}/revoke)
saperly_post_workspaces_by_slug_invitationsWRITEInvite a member by email. `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". (POST https://api.saperly.com/workspaces/{slug}/invitations)
saperly_post_workspaces_by_slug_webhooksWRITECreate a webhook endpoint. Registers an outbound webhook endpoint and returns it with its full `signingSecret`. Use the secret to verify each delivery’s `x-saperly-signature: v1=<hex>` header (HMAC-SHA256 over `${x-saperly-timestamp}.${rawBody}`). `slug` must be the workspace this connection's key belongs to: measured 2026-09-16, the key reaches these routes, and any OTHER slug answers 403 {"_tag":"AuthorizationDenied","reason":"token is not scoped to this workspace"} -- a distinct reason from the bogus-key "invalid_or_insufficient_token". SIGNING SECRET IS REDACTED: Saperly returns the new endpoint's `signingSecret` here and this integration replaces it with a placeholder, for the reason above. The webhook IS created and every other field is real; read its secret in Saperly: Settings > Webhooks. (POST https://api.saperly.com/workspaces/{slug}/webhooks)
Often connected alongside
Put Saperly behind one governed endpoint.
Same permissions, same audit trail, whatever else you connect next.