Listening …
Technical reference
Ask whether a recipient is opted out before you contact them. The call carries a hash, not a person: we can neither read the number nor reverse it. The answer is yes or no, plus a receipt number you can produce later.
The API has two endpoints. /v1/suppression/check answers for one recipient at a time and is used in real time — for example in a dialing queue or right before a send. /v1/suppression/batch takes a list and is used when a campaign is being prepared.
Both accept hashed identifiers. No endpoint returns personal data, and no endpoint confirms that a person exists in our system — only that the hash shouldn't be contacted.
A response with suppressed: false means we don't know of an opt-out. It isn't consent, and it doesn't replace your own lawfulness assessment.
Every call carries a key in the Authorization header. Keys are environment-bound: mm_test_ for the test environment, mm_live_ for production. A key used against the wrong environment is rejected with 401 instead of silently falling back.
Authorization: Bearer mm_live_…Content-Type: application/jsonIdempotency-Key: <uuid v4> # recommended for every write callThe hash is computed on your side, with a pepper unique to your organization. That does two things: we can't reconstruct the contact detail, and a leak of your hashes can't be cross-referenced against anyone else's.
# 1. Normalize to E.164 before anything else 070-123 45 67 -> +46701234567 Name@Example.COM -> name@example.com # 2. Hash with your assigned pepper (never in the client) sha256(pepper + normalized) # 3. Send the hash, never the value "identifier_hash": "sha256:9f1a3c…c07d"Normalization has to happen before hashing. 070-123 45 67 and +46 70 123 45 67 are the same subscriber but different strings — without normalization they hash differently and the check misses the opt-out.
Used per recipient, in real time. Response time is designed to fit inside a call flow, so the check can sit directly before the dial.
POST /v1/suppression/check HTTP/1.1Host: api.marketmute.comAuthorization: Bearer mm_live_…Content-Type: application/jsonIdempotency-Key: 8c1f0b52-3a77-4a1e-9f0c-2b7d4e11ab90 { "channel": "phone", "identifier_hash": "sha256:9f1a3c…c07d", "purpose": "direct_marketing", "sender_ref": "campaign-2026-h2"}HTTP/1.1 200 OKContent-Type: application/jsonRateLimit-Remaining: 986 { "suppressed": true, "channel": "phone", "scope": "sender", "effective_from": "2026-03-04T09:12:00Z", "receipt_id": "rcpt_01HZK4Q2M8V3XT", "checked_at": "2026-08-19T07:41:02Z"}Store receipt_id alongside your send log. It's the only reference that ties your check to our answer if the outcome is ever questioned.
Send up to 10,000 hashes per call. The result list always has the same order and the same length as identifier_hashes, so you can index straight across without matching on values.
POST /v1/suppression/batch HTTP/1.1Authorization: Bearer mm_live_…Content-Type: application/json { "channel": "phone", "purpose": "direct_marketing", "identifier_hashes": [ "sha256:9f1a3c…c07d", "sha256:41b8ee…9a20" ]} # Response: same order as the request, never reordered{ "results": [ { "suppressed": true, "scope": "sender" }, { "suppressed": false, "scope": null } ], "receipt_id": "rcpt_01HZK4Q2M8V44P"}Larger lists are split by you, not by us. A call that exceeds the cap is rejected with 400 instead of being silently truncated.
| Field | Type | Meaning |
|---|---|---|
| suppressed | boolean | True if an opt-out applies to the hash on the given channel and purpose. |
| scope | string | null | sender = the opt-out applies to your organization. global = the opt-out applies to all direct marketing. |
| effective_from | string (RFC 3339) | When the opt-out took effect. Absent when suppressed is false. |
| receipt_id | string | Receipt number for the check. Proves that you asked, when you asked, and what answer you got. |
| checked_at | string (RFC 3339) | The server's timestamp for the answer. Use this, not your own clock, in logs. |
suppressed: true — don't contact the hash on that channel.suppressed: false — no known opt-out. Your own assessment still applies.All errors are returned as JSON with the fields error and message. The status code is authoritative — read it before the body.
RateLimit-Remaining.Idempotency-Key and the attempt only counts once.The test key is available immediately and mirrors production with synthetic hashes. The production key requires the sender to be registered, with a contact channel for rights requests on file.