Create a registration and run the website disclosure check

POST /v1/registrations

Fetches the website, privacy policy, terms (discovered from homepage links when not given) and opt-in page, and checks them against carrier requirements. Also checks your example messages (sample_messages). Returns status ready or checks_failed, with findings, guidance, and generated copy. A registration is ready only when the website check passes, the example check passes, and the examples are yours (samples_source = customer): without sample_messages you get starter drafts that must be edited or confirmed first.

Send your API key as a bearer token: Authorization: Bearer ssms_sk_.... Test keys run this endpoint against the sandbox; see Authentication for key modes and scopes.

Request body

JSON (Content-Type: application/json).

A RegistrationInput object.

FieldTypeRequiredDescription
business_namestringYesAt most 120 characters.
websitestring (uri)Yes
use_case2fa, account_notification, customer_care, delivery_notification, fraud_alert, higher_education, marketing, mixed, polling_voting, public_service_announcement, security_alert, low_volumeYes
opt_in_urlstring (uri)NoPage where people enter their number
privacy_urlstring (uri)NoDiscovered from homepage links when omitted
terms_urlstring (uri)NoDiscovered from homepage links when omitted
support_emailstring (email)NoUsed in the generated HELP reply
sample_messagesarray of stringNoReal examples of the texts you will send, as a recipient would get them (no template variables). Carriers compare them with your traffic. Each must name your business; marketing and mixed use cases need opt-out wording in at least one. More than 5, or one over 320 characters, is a 400; too few or too short comes back as findings. Omit to get starter drafts; null or [] goes back to them. At most 5 items. At least 2 items. Nullable.
starter_messagesarray of stringNoDraft examples suggested for this business (fields.sample_messages from POST /registrations/prefill). Shown instead of the template starter drafts; samples_source stays starter and they are never filed until you edit or confirm them. Drafts that would not pass the example check for the business name and use case are ignored. At most 5 items. Nullable.
samples_confirmedbooleanNotrue = the starter drafts are right as they are; they become your examples. Ignored when sample_messages is sent.
descriptionstringNoWhat you send and to whom (40 to 500 characters). Omit or null to file the generated one. At most 500 characters. Nullable.
brandobjectNo
brand.legal_namestringNo
brand.entity_typePRIVATE_PROFIT, PUBLIC_PROFIT, NON_PROFIT, GOVERNMENT, SOLE_PROPRIETORNo
brand.einstringNoExample: 12-3456789.
brand.streetstringNo
brand.citystringNo
brand.statestringNo
brand.postal_codestringNo
brand.countrystringNo
brand.verticalstringNo
brand.contact_emailstringNo
brand.contact_phonestringNo

Responses

StatusMeaningBody
201RegistrationRegistration
400Invalid inputError
401Missing, malformed, or revoked API keyError
402Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or chargedError
429Rate limit or quota exceededError

201: Registration fields

FieldTypeDescription
idstringExample: reg_a1B2c3D4e5F6.
objectregistration
statusdraft, checks_failed, ready, submitted, in_review, approved, rejected
business_namestring
websitestring
use_casestring
opt_in_urlstringNullable.
privacy_urlstringNullable.
terms_urlstringNullable.
support_emailstringNullable.
brandobject
sample_messagesarray of stringThe examples as they stand: yours, or starter drafts (see samples_source). These are what a submission files.
samples_sourcestarter, customerstarter: our drafts, not yet edited or confirmed; the registration cannot be ready. customer: yours.
descriptionstringYour own description; null when the generated one (generated.campaign.description) is filed Nullable.
sample_checkobjectThe example-message check. Deterministic rules, no AI. Recomputed on every read.
sample_check.passedinteger
sample_check.failedinteger
sample_check.warningsinteger
sample_check.okboolean
sample_check.findingsarray of SampleFinding
guidanceobjectThe five answers every state gives
guidance.what_happenedstring
guidance.whystring
guidance.next_actoryou, delivered, carriers, nobody
guidance.next_stepstring
guidance.afterstring
checkobjectNullable.
check.checked_atstring (date-time)
check.passedinteger
check.failedinteger
check.warningsinteger
check.urlsobject
check.findingsarray of Finding
rejectionobjectNullable.
rejection.codestring
rejection.titlestring
rejection.explanationstring
rejection.fixstring
rejection.notestringNullable.
rejection.related_findingsarray of string
rejection.rejected_atstring (date-time)
generatedobjectDeterministic compliant copy and the registry draft
generated.opt_in_ctastring
generated.privacy_clausestring
generated.sms_termsstring
generated.sample_messagesarray of stringStarter drafts from templates. What is filed is the top-level sample_messages (also in generated.campaign.sample_messages). At most 5 items. At least 2 items.
generated.help_replystring
generated.stop_replystring
generated.opt_in_confirmationstring
generated.brandobject
generated.brand.missingarray of string
generated.campaignobject
generated.campaign.use_casestring
generated.campaign.descriptionstring
generated.campaign.message_flowstring
generated.campaign.sample_messagesarray of string
generated.campaign.opt_in_keywordsarray of string
generated.campaign.opt_in_messagestring
generated.campaign.opt_out_keywordsarray of string
generated.campaign.opt_out_messagestring
generated.campaign.help_keywordsarray of string
generated.campaign.help_messagestring
generated.campaign.embedded_linkboolean
generated.campaign.embedded_phoneboolean
generated.campaign.number_poolingboolean
generated.campaign.direct_lendingboolean
generated.campaign.age_gatedboolean
generated.campaign.affiliate_marketingboolean
generated.campaign.privacy_policy_urlstring
generated.campaign.terms_urlstring
submissionsinteger
historyarray of object
history[].atstring (date-time)
history[].statusstring
history[].actorcustomer, delivered, system
history[].labelstring
history[].reason_codestring
history[].notestring
created_atstring (date-time)
updated_atstring (date-time)

Errors

StatusWhen
400Invalid input
401Missing, malformed, or revoked API key
402Billable live calls only: the prepaid credit balance cannot cover the call (insufficient_credits); nothing was done or charged
429Rate limit or quota exceeded

Every error has the same JSON shape, and request_id matches the X-Request-Id response header. Errors lists every code and what to do about it.

json
{
  "error": {
    "code": "invalid_request",
    "message": "What went wrong, in plain words.",
    "param": "the_field",
    "request_id": "req_a1B2c3D4e5F6g7H8"
  }
}

Examples

curl

bash
curl -X POST "https://api.joinsimplesms.com/v1/registrations" \
  -H "Authorization: Bearer $SIMPLESMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "business_name": "string",
  "website": "https://example.com",
  "use_case": "2fa"
}'

Node.js

The Node.js SDK does not wrap this endpoint yet; call it with fetch.

javascript
const res = await fetch('https://api.joinsimplesms.com/v1/registrations', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SIMPLESMS_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "business_name": "string",
    "website": "https://example.com",
    "use_case": "2fa"
  }),
});

if (!res.ok) throw new Error((await res.json()).error.message);
const data = await res.json();

Python

The Python SDK does not wrap this endpoint yet; call it over HTTP.

python
import json, os, urllib.request

req = urllib.request.Request(
    "https://api.joinsimplesms.com/v1/registrations",
    method="POST",
    headers={
        "Authorization": f"Bearer {os.environ['SIMPLESMS_API_KEY']}",
        "Content-Type": "application/json",
    },
    data=json.dumps({
        "business_name": "string",
        "website": "https://example.com",
        "use_case": "2fa"
    }).encode(),
)
with urllib.request.urlopen(req) as res:
    data = json.load(res)