saperly
Guides

Compliance

Consent, disclosures, and 10DLC registration are first-class in Saperly, with consent and disclosures enforced independently of any LLM — outbound contact without recorded consent is blocked before it goes out.

Saperly bakes TCPA compliance in. Consent, disclosures, and 10DLC registration are first-class objects, not prompt instructions. Consent and disclosures are enforced independently of any LLM — a misbehaving model (or one you swap out) can never route around them, and outbound voice or SMS to a peer without recorded consent is rejected before it goes out. 10DLC is the carrier-side registration Saperly files and tracks on your behalf; carriers, not Saperly, police delivery on unregistered numbers.

Base URL https://api.saperly.com. Authenticate with Authorization: Bearer sap_sk_live_...; the workspace is resolved from the token.

Consent is recorded per (number, peer) pair: a ConsentRecord says this Saperly number has consent to contact that peer number. Saperly checks it automatically on every outbound message and call.

ConsentTypeMeaning
implied_inboundThe peer contacted you first (an inbound call or SMS), which implies consent to reply.
explicit_outboundThe peer explicitly opted in to be contacted — required before you initiate outbound contact.

Endpoints

MethodPathReturnsScope
GET/consentConsentRecord[]read
POST/consentConsentRecord (201)write
POST/consent/revokeConsentRecordwrite
GET/consent/check?numberId=&peerNumber={ hasConsent, type? }read

The ConsentRecord shape

FieldTypeNotes
idstringRecord id.
numberIdstringThe Saperly number.
peerNumberstringThe peer, in E.164 (e.g. +15555550123).
consentTypeConsentTypeimplied_inbound or explicit_outbound.
sourcestringFree-form provenance label, e.g. "sms_optin" or "call_recording_disclosure".
grantedAtstringISO 8601 timestamp.
revokedAtstring | nullISO 8601 timestamp once revoked, else null.

Worked flow: check, record, then contact

Always check consent before initiating outbound contact, record explicit_outbound consent when the peer opts in, then send. The source label is your audit trail — make it describe how consent was obtained.

# 1. Check before contacting
curl "https://api.saperly.com/consent/check?numberId=num_...&peerNumber=%2B15555550123" \
  -H "Authorization: Bearer $SAPERLY_API_KEY"
# -> { "hasConsent": false }

# 2. The peer opts in — record explicit outbound consent
curl -X POST https://api.saperly.com/consent \
  -H "Authorization: Bearer $SAPERLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "numberId": "num_...",
    "peerNumber": "+15555550123",
    "consentType": "explicit_outbound",
    "source": "sms_optin"
  }'

# 3. Now the send/call is allowed (see Messaging / Voice)
import { configure, consent, messaging } from '@trysaperly/sdk'

configure({ apiKey: process.env.SAPERLY_API_KEY! })

// 1. Check (query params)
const { data: checked } = await consent.check({
  query: { numberId: 'num_...', peerNumber: '+15555550123' },
})

// 2. Record on opt-in
if (checked && !checked.hasConsent) {
  await consent.record({
    body: {
      numberId: 'num_...',
      peerNumber: '+15555550123',
      consentType: 'explicit_outbound',
      source: 'sms_optin',
    },
  })
}

// 3. Now send — see Messaging
const { error } = await messaging.send({
  body: {
    fromNumberId: 'num_...',
    to: '+15555550123',
    body: 'Thanks for opting in!',
  },
})
if (error) {
  // e.g. error._tag === 'RecipientOptedOut' (403) if the peer opted out
}

Outbound to an opted-out peer is blocked

Initiating an outbound call or SMS to a peer who has opted out is a TCPA violation — Saperly blocks it for you and the request fails with the typed error RecipientOptedOut (403). Record explicit_outbound consent before you initiate contact, or rely on implied_inbound consent created when the peer contacts you first.

POST /consent/revoke with { numberId, peerNumber } sets revokedAt and stops further outbound contact. Two things revoke consent without an explicit call:

  • STOP — an inbound SMS with the STOP keyword automatically revokes the sender's consent. Subsequent outbound sends to that number are blocked.
  • HELP — an inbound HELP keyword is logged (it does not change consent).
curl -X POST https://api.saperly.com/consent/revoke \
  -H "Authorization: Bearer $SAPERLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "numberId": "num_...", "peerNumber": "+15555550123" }'

Disclosures

A disclosure is the AI notice spoken at the start of a call so the caller is told they are talking to an AI. It lives on the connection, not as a separate resource: each connection has complianceEnabled (a boolean, on by default) and a disclosure string. When compliance is on, the disclosure is spoken as the line's first, uninterruptible utterance — the TCPA notice — before the agent takes a turn, across every mode (hosted, manual, and OpenAI-realtime).

import { configure, connections } from '@trysaperly/sdk'

configure({ apiKey: process.env.SAPERLY_API_KEY! })

// The disclosure is a field on the connection — set it when you create or update one
await connections.create({
  body: {
    name: 'support line',
    instructions: 'You are a friendly support agent for Acme Inc.',
    complianceEnabled: true,
    disclosure: 'You are speaking with an AI assistant for Acme.',
  },
})

Leave disclosure empty with complianceEnabled on and Saperly fills a standard, org-named default so the line is never silently non-disclosing. Set complianceEnabled to false and there is no forced opener. When a recorded disclosure is the basis for contact, record it as a consent source (e.g. "call_recording_disclosure") so the provenance is captured in your audit trail.

10DLC

10DLC is the US carrier registration that certifies application-to-person (A2P) SMS. Registering is what lets your US SMS deliver reliably instead of being filtered, and it happens in three steps on the dashboard's A2P / 10DLC page:

  1. Register a brand — the legal entity behind your messaging, verified by the registry (one-time registration fee, charged from your balance).
  2. Register campaigns under a verified brand — what you send and how subscribers consent (a monthly fee per campaign, quoted before you submit; the first month is charged at submission and fees continue during carrier review).
  3. Assign numbers to an approved campaign — one registration per number; a number carries compliant A2P traffic only while assigned.

Saperly tracks every registration and advances its status automatically as registry/carrier decisions arrive.

Brand status

StatusMeaning
draftEditable locally; nothing filed, nothing charged.
submittedFiled with the registry, identity verification in progress.
submittingSubmission in flight — resolves automatically (refresh if it looks stuck).
verifiedIdentity verified — campaigns can be registered.
vettedVerified with enhanced vetting (higher carrier throughput).
failedVerification failed — fix the details and resubmit (no second fee).

Campaign status

StatusMeaning
draftEditable locally; nothing filed, nothing charged.
submitted / pending_reviewFiled; registry + carrier review in progress.
submittingSubmission in flight — resolves automatically.
approvedCertified — assign numbers and send A2P SMS.
declinedThe review declined the campaign — edit and resubmit (a resubmission is a new registration and is charged again).
suspendedA previously approved campaign was suspended by a carrier. Fees still accrue until you deactivate.
deactivatedYou retired the campaign — no further fees.

Number assignment status

StatusMeaning
pendingAssignment filed with the carrier network.
assignedThe number is cleared to send under the campaign.
failedThe assignment failed — the reason is shown; fix and retry.

Before you file, read the approval guide

Reviewers check registrations against a specific rubric — Getting your A2P registration approved walks every requirement (opt-in evidence, message templates, brand-vetting pitfalls) so your first submission passes. Declined campaign fees are not refunded.

10DLC is a dashboard flow with automatic status updates

Manage brands, campaigns, and number assignments on the dashboard's A2P / 10DLC page. Statuses advance automatically as the registry and carriers decide (with a daily reconciliation safety net) — use the page's Refresh actions for an on-demand check.

  • Messaging — SMS sends; consent is enforced on every outbound message.
  • Voice — outbound calls; consent is enforced before the call starts.
  • Webhooks — receive STOP/HELP, delivery receipts, and 10DLC status events.
  • Core concepts — where compliance sits in the Saperly model.
  • API reference — every consent endpoint with a try-it playground.

On this page