A2P 10DLC pre-scan API
Add A2P 10DLC campaign pre-scanning to your messaging workflows: validate descriptions, sample messages, opt-in flows, and policy URLs over JSON — one campaign per call, scripted across your pipeline — before they're registered through Twilio, Plivo, Vonage, or any other provider.
The API checks campaign readiness; registration itself always happens with your provider. New to the review process? Start with what A2P verification means, or try a one-off scan in the free scanner.
Machine-readable specs are published: /openapi.json (OpenAPI 3.1) and /llms-full.txt (full field reference for AI agents). Building an agent or coding harness? See the agent guide.
Wiring the API into a provider-specific flow? See what each provider's registration reviews before you script the submission: Twilio, Plivo, Vonage, or the full provider list. The API checks readiness; registration stays with your provider.
For agencies, consultants & platform teams
If you register A2P 10DLC campaigns on behalf of clients — or your product onboards businesses that do — use the API to pre-scan every campaign before it reaches Twilio / TCR. Common patterns: gate submission on a GREEN report, surface per-field findings inline in your onboarding form, or store the scan output for client audit trails.
Create a self-serve API key from your developer dashboard and pass it as a bearer token for headless / API access. Scans run against your account and spend the same credits as browser scans: one for Basic and two for Full. There is no separate key fee or API subscription. Compare public prices. Anonymous/basic validation still works without a key for low-volume checks.
Create an API keyAuthentication
Bearer auth is optional for Basic only. Explicit Full without a key returns 401 AUTH_REQUIRED, not a downgrade. Authenticated Basic uses one credit; Full uses two. A human must approve tier, cost and total budget before any authenticated call. Create a self-serve key from your developer dashboard and send it as a bearer token to run full scans against your account's credits. The full key is shown once at creation — we store only a hash, so save it then.
Authorization: Bearer a2p_live_your_api_key_here
Endpoints
/api/agent/validateStable JSON endpoint for AI agents and coding harnesses. Body: { tier?, campaign, campaignName?, idempotencyKey? }. Omitted tier defaults to Basic; requesting Full requires auth. Success wraps the verdict under result.overallTier, with scanId, reused, and creditsSpent outside the report. Documented in /openapi.json.
/api/v1/scan · /api/v1/scan/quickThese are the underlying worker routes used by the hosted app. Agents should prefer /api/agent/validate, which keeps the payload stable and same-origin.
Campaign object
These fields belong inside the required campaign object, not directly at the top of the POST body. See the complete examples below. Exact use-case values, including TWO_FACTOR_AUTH, are listed in the OpenAPI contract.
{
"useCaseType": "MARKETING", // required
"campaignDescription": "...", // required
"sampleMessages": ["msg1", "msg2"], // required, 2-5 items
"messageFlow": "...", // required
"businessName": "Acme Inc", // optional
"privacyPolicyUrl": "https://...", // optional
"websiteUrl": "https://...", // optional
"termsOfServiceUrl": "https://...", // optional
"optInKeywords": ["START"], // optional
"optOutKeywords": ["STOP"], // optional
"helpKeywords": ["HELP"], // optional
"optInMessage": "...", // optional
"optOutMessage": "...", // optional
"helpMessage": "...", // optional
"embeddedLinks": true, // optional
"embeddedPhoneNumbers": false, // optional
"ageGatedContent": false, // optional
"directLending": false, // optional
"numberPool": false // optional
}Anonymous Basic example
Fictional request, not an approved campaign. Basic does not fetch external URLs.
curl --fail-with-body --max-time 75 -sS -X POST https://www.a2pcheck.com/api/agent/validate \
-H "Content-Type: application/json" \
-d '{
"tier": "basic",
"campaign": {
"useCaseType": "MARKETING",
"businessName": "Acme Coffee Co.",
"campaignDescription": "Acme Coffee Co. sends weekly promotional offers and new-product announcements to customers who opt in at checkout or on our website. Messages link to acme.example.com and always include opt-out instructions.",
"sampleMessages": [
"Acme Coffee: 20% off all beans this week. Shop acme.example.com/sale. Msg&data rates may apply. Reply STOP to opt out, HELP for help.",
"Acme Coffee: New single-origin just dropped. Browse acme.example.com/new. Reply STOP to unsubscribe."
],
"messageFlow": "Customers opt in by checking an unchecked SMS-consent box at checkout on acme.example.com or by texting START to our number. Consent is not a condition of purchase.",
"privacyPolicyUrl": "https://acme.example.com/privacy",
"websiteUrl": "https://acme.example.com",
"termsOfServiceUrl": "https://acme.example.com/terms",
"optInKeywords": [
"START"
],
"optOutKeywords": [
"STOP"
],
"helpKeywords": [
"HELP"
],
"optInMessage": "Acme Coffee: You'"'"'re subscribed to weekly offers. Reply STOP to opt out, HELP for help. Msg&data rates may apply.",
"optOutMessage": "Acme Coffee: You'"'"'re unsubscribed and won'"'"'t receive more messages. Reply START to rejoin.",
"helpMessage": "Acme Coffee: For help, email [email protected]. Reply STOP to opt out.",
"embeddedLinks": true,
"embeddedPhoneNumbers": false
}
}'Authenticated Full example
Replace fictional facts/URLs before spending. Get explicit approval for one paid request; another scan needs renewed approval. Supply A2PCHECK_API_KEY securely from your environment. Set A2PCHECK_REQUEST_ID to a unique per-version ID (1–128 letters, digits, hyphens or underscores); the example sends it as idempotencyKey. Retain it only for the exact same campaign version/tier when retrying; successful same-key replays within 24 hours are not charged again. Failed/refunded scans can be billed again. Do not enable automatic paid retries. See the agent guide for the full workflow.
: "${A2PCHECK_API_KEY:?Set your API key securely before running a paid scan}"
: "${A2PCHECK_REQUEST_ID:?Set a unique ID for this exact campaign version; retain it only for retries}"
case "$A2PCHECK_REQUEST_ID" in
*[!A-Za-z0-9_-]*) printf '%s\n' "Request ID must contain only letters, digits, underscores or hyphens." >&2; exit 1 ;;
esac
if [ "${#A2PCHECK_REQUEST_ID}" -gt 128 ]; then
printf '%s\n' "Request ID must be at most 128 characters." >&2
exit 1
fi
curl --fail-with-body --max-time 75 -sS -X POST https://www.a2pcheck.com/api/agent/validate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $A2PCHECK_API_KEY" \
-d '{
"tier": "full",
"campaign": {
"useCaseType": "MARKETING",
"businessName": "Acme Coffee Co.",
"campaignDescription": "Acme Coffee Co. sends weekly promotional offers and new-product announcements to customers who opt in at checkout or on our website. Messages link to acme.example.com and always include opt-out instructions.",
"sampleMessages": [
"Acme Coffee: 20% off all beans this week. Shop acme.example.com/sale. Msg&data rates may apply. Reply STOP to opt out, HELP for help.",
"Acme Coffee: New single-origin just dropped. Browse acme.example.com/new. Reply STOP to unsubscribe."
],
"messageFlow": "Customers opt in by checking an unchecked SMS-consent box at checkout on acme.example.com or by texting START to our number. Consent is not a condition of purchase.",
"privacyPolicyUrl": "https://acme.example.com/privacy",
"websiteUrl": "https://acme.example.com",
"termsOfServiceUrl": "https://acme.example.com/terms",
"optInKeywords": [
"START"
],
"optOutKeywords": [
"STOP"
],
"helpKeywords": [
"HELP"
],
"optInMessage": "Acme Coffee: You'"'"'re subscribed to weekly offers. Reply STOP to opt out, HELP for help. Msg&data rates may apply.",
"optOutMessage": "Acme Coffee: You'"'"'re unsubscribed and won'"'"'t receive more messages. Reply START to rejoin.",
"helpMessage": "Acme Coffee: For help, email [email protected]. Reply STOP to opt out.",
"embeddedLinks": true,
"embeddedPhoneNumbers": false
},
"idempotencyKey": "'"$A2PCHECK_REQUEST_ID"'"
}'Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Malformed body / invalid tier / missing campaign |
| 401 | INVALID_API_KEY | API key is missing, unknown, or revoked |
| 401 | AUTH_REQUIRED | This scan tier needs a signed-in account or key |
| 402 | INSUFFICIENT_CREDITS | Account needs more scan credits |
| 429 | ANON_SCAN_LIMIT | Anonymous per-IP daily limit hit |
| 429 | RATE_LIMITED | Scanner busy; respect the returned retry delay |
| 400 / 5xx | SCAN_FAILED | Report failure; confirm status and budget before a paid retry |
Headless API access
Self-serve API keys are live. Create one from your developer dashboard and send it as a bearer token. The anonymous basic endpoint still works without a key for low-volume agent checks.
Create an API key