PHS|PP marketing partner
API documentation
Developer reference

Suppression API

Check phone numbers against our suppression lists before dialing, and manage entries on the lists your API key is scoped to. One base URL, bearer-key auth, JSON in and out.

https://phs.persistentpolicies.com/v1

Authentication

Every request carries your API key in the Authorization header. Keys are issued by Persistent Policies and are scoped: a key can read the eligibility check, write to specific lists, or both. Requests outside your key's scope return 403.

Authorization: Bearer YOUR_API_KEY

Keep keys confidential and out of client-side code. If a key leaks, contact Persistent Policies for rotation, revocation is immediate.

Identify your traffic

Add a name query parameter with your company's short label to every request. It is optional today but strongly recommended: if your key is ever misconfigured, it is how we know the failing traffic is yours and can reach you before your unscrubbed dials become a problem. Letters, digits, dash, and underscore only, up to 32 characters.

curl "https://phs.persistentpolicies.com/v1/check/8135551234?name=ACMELEADS" \ -H "Authorization: Bearer YOUR_API_KEY"

Check eligibility

The core call: is this number OK to dial? A single request checks the number against every suppression list at once.

Single number

curl "https://phs.persistentpolicies.com/v1/check/8135551234" \ -H "Authorization: Bearer YOUR_API_KEY" {"cid": "8135551234", "eligible": true, "lists": []}

Suppressed number

{"cid": "8135551234", "eligible": false, "lists": ["dnc"]}

Batch, up to 1,000 numbers

curl -X POST "https://phs.persistentpolicies.com/v1/check" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"cids": ["8135551234", "7275550100"]}' { "results": {"8135551234": true, "7275550100": false}, "lists": {"7275550100": ["sales"]} }

Values that can appear in lists:

ValueMeaning
phsLong-term suppression. No expiry.
salesRecent sale (Sold7D). Rolls off 7 days after the last add.
aca90dACA suppression. Rolls off after 90 days.
dncDo Not Call. Permanent until removed by the managing partner.

Treat eligible: false as do-not-dial regardless of which list matched. Responses include x-phspp-cache and x-phspp-colo debug headers so you can verify edge behavior from your side.

Add numbers

Write endpoints live at /v1/add/<list>. The list segment you use is the one named in your integration guide or key issuance, for example Sold7D or your assigned DNC bucket. Your key must hold write scope for that list.

Single number, GET form

curl "https://phs.persistentpolicies.com/v1/add/Sold7D?cid=8135551234" \ -H "Authorization: Bearer YOUR_API_KEY" {"list": "Sold7D", "cid": "8135551234", "added": 1}

Optional &source= and &notes= query params attach provenance to the entry. Re-adding a number already on a rolling list returns duplicate: true and refreshes its expiry window.

Batch, POST form, up to 1,000 numbers

curl -X POST "https://phs.persistentpolicies.com/v1/add/DNC1" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"cids": ["8135551234", "7275550100"]}' {"list": "DNC", "added": 2, "unchanged": 0}

The response reports how many were added, how many were already present, and any rows that could not be parsed. Note: Sold7D adds are GET-only, and PHS adds are POST-only. Using the wrong verb returns a 405 with a message pointing at the right one.

Remove numbers

Removes are always POST, same body shape as a batch add. Removal rights are scoped separately from adds: most vendor keys can add but not remove, and DNC keys can remove only within their own bucket.

curl -X POST "https://phs.persistentpolicies.com/v1/remove/DNC1" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"cids": ["8135551234"]}' {"list": "DNC", "removed": 1, "not_present": 0}

Removal takes effect on the next check. Numbers not on the list are counted in not_present, never an error.

Bulk export

A nightly snapshot of every suppressed number as SHA-256 hashes, so you can scrub whole dial lists locally instead of checking numbers one at a time. Requires a key with export access. Prefer a browser? The same file is available at the vendor portal behind your portal sign-in.

Download the snapshot

curl "https://phs.persistentpolicies.com/v1/suppression/export" \ -H "Authorization: Bearer YOUR_API_KEY" \ -o suppression.txt

One lowercase-hex hash per line, LF-separated, no header. The input to each hash is the 10-digit US number, digits only, no +1: sha256("5551234567"). Hash your own list the same way and drop every number whose hash appears in the file.

Snapshot metadata

curl "https://phs.persistentpolicies.com/v1/suppression/export/meta" \ -H "Authorization: Bearer YOUR_API_KEY" {"generated_at": "…", "count": 812345, "bytes": 52802425, "sha256": "…", "format": "…"}

A fresh snapshot generates daily at midnight US-Eastern. The download also carries X-Export-Count and X-Export-Generated-At headers. Limits: 24 downloads per key per UTC day (429 past that); a 503 export_not_generated_yet means the snapshot is still building, retry shortly. Some keys are scoped to a subset of lists, in which case the file covers exactly those lists. The export is confidential under your Marketing Partner Agreement: scrubbing use only, no resale or redistribution.

Number format

  • 10-digit US numbers. Punctuation and a leading 1 or +1 are fine: +1 (813) 555-1234 and 8135551234 are the same entry.
  • Invalid entries never fail a batch. They are dropped, counted, and reported in the errors array with a per-row reason.
  • Adding a number that is already on a list is a safe no-op.

Freshness and caching

The API is built so staleness can only ever over-block, never leak a newly suppressed number back into your dial queue:

  • Eligible verdicts are never cached. Every "OK to dial" answer is computed fresh, so a number suppressed seconds ago stops passing on the very next check. Allow up to about 60 seconds for full worldwide propagation.
  • Ineligible verdicts cache for up to an hour at the edge. After a removal, a number may briefly keep reading as suppressed. That direction of staleness is safe, so it is allowed.
  • Do not cache eligibility results on your side. The response's cache-control headers already encode the rules above.

Errors and limits

Errors are JSON with an error code:

StatusCodeMeaning
400invalid_phone / invalid_body / missing_cidMalformed number or request body.
400batch_too_largeBatches are capped at 1,000 numbers per request. Split and resend.
401missing_auth / invalid_or_revoked_keyMissing, invalid, or revoked API key.
403insufficient_scopeYour key cannot perform this action on this list.
404unknown_listThe list segment in the URL is not recognized.
405method_not_allowedWrong verb for this list's add path. The message names the right one.

Health check, no auth required: GET https://phs.persistentpolicies.com/v1/health. Questions, key requests, or rotation: contact Persistent Policies, or contact@persistentpolicies.com.