Unsubscribes and blocks
Who may not be emailed and why: unsubscribes, resubscribes with consent, manual blocks and unblocks.
Unsubscribe an address
POST/projects/{project_id}/unsubscribes
The address gets no more campaigns or broadcasts. Transactional email still goes out. Works for addresses that are not contacts too.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| project_id | path | string | Required |
Body
Send JSON with every field; use null for a value you do not set.
| Field | Type | Notes |
|---|---|---|
| string | Required |
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/unsubscribes" \
-H "Authorization: Bearer $SENDOCADO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "maya@example.com"
}'{
"id": "qoaa8t3rup47p9pb0tdbm50fqo1xo5cv",
"email": "maya@example.com",
"source": "api",
"created_at": 1790000000000
}Errors
| Status | Type | Example message |
|---|---|---|
| 400 | invalid_data | Send a JSON request body. |
| 401 | unauthorized | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | project_not_found | Project not found. |
| 429 | rate_limited | Too many requests. Try again in 12 seconds. |
Resubscribe an address
POST/projects/{project_id}/resubscribes
Opts an address back in. It needs proof that the person asked, so every resubscribe records the consent it rests on.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| project_id | path | string | Required |
Body
Send JSON with every field; use null for a value you do not set.
| Field | Type | Notes |
|---|---|---|
| string | Required | |
| consent | object | Required |
| consent.wording | string | null | Required |
| consent.ip_address | string | null | Required |
| consent.user_agent | string | null | Required |
| consent.consented_at | integer | Required |
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/resubscribes" \
-H "Authorization: Bearer $SENDOCADO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "maya@example.com",
"consent": {
"wording": "Yes, send me product updates from Acme.",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6)",
"consented_at": 1789999940000
}
}'{
"id": "0xzmas6en5mtmo3oqsg5lo50djzdnbj0",
"email": "maya@example.com",
"consent_id": "ddlz2uhfkvml73ctyxv2kgafrfw0h9ny",
"created_at": 1790000000000
}Errors
| Status | Type | Example message |
|---|---|---|
| 400 | invalid_data | Send a JSON request body. |
| 401 | unauthorized | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | project_not_found | Project not found. |
| 429 | rate_limited | Too many requests. Try again in 12 seconds. |
Block an address
POST/projects/{project_id}/manual-blocks
The address gets no email at all, transactional included, until you unblock it.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| project_id | path | string | Required |
Body
Send JSON with every field; use null for a value you do not set.
| Field | Type | Notes |
|---|---|---|
| string | Required | |
| reason | string | null | Required |
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/manual-blocks" \
-H "Authorization: Bearer $SENDOCADO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "spamtrap@example.com",
"reason": "Known spam trap"
}'{
"id": "wt1fd4mx82mux4b0pzcyc3edqmevxrvc",
"email": "spamtrap@example.com",
"reason": "Known spam trap",
"created_at": 1790000000000
}Errors
| Status | Type | Example message |
|---|---|---|
| 400 | invalid_data | Send a JSON request body. |
| 401 | unauthorized | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | project_not_found | Project not found. |
| 429 | rate_limited | Too many requests. Try again in 12 seconds. |
Unblock an address
POST/projects/{project_id}/unblocks
Clears the bounces, spam complaints and manual blocks recorded so far. An address that reported spam needs consent to be unblocked. An unsubscribe is cleared only by a resubscribe.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| project_id | path | string | Required |
Body
Send JSON with every field; use null for a value you do not set.
| Field | Type | Notes |
|---|---|---|
| string | Required | |
| reason | string | null | Required |
| consent | object | null | Required |
| consent.wording | string | null | Required |
| consent.ip_address | string | null | Required |
| consent.user_agent | string | null | Required |
| consent.consented_at | integer | Required |
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/unblocks" \
-H "Authorization: Bearer $SENDOCADO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "maya@example.com",
"reason": "Mailbox was full, now fixed",
"consent": null
}'{
"id": "qurtaebog43yq15i5latjpuu3xf6mzkp",
"email": "maya@example.com",
"reason": "Mailbox was full, now fixed",
"consent_id": null,
"created_at": 1790000000000
}Errors
| Status | Type | Example message |
|---|---|---|
| 400 | invalid_data | Send a JSON request body. |
| 401 | unauthorized | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | project_not_found | Project not found. |
| 429 | rate_limited | Too many requests. Try again in 12 seconds. |
Record consent
POST/projects/{project_id}/consents
Proof that an address opted in: the wording they agreed to, where from, and when. Kept as a record; it does not change who gets email.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| project_id | path | string | Required |
Body
Send JSON with every field; use null for a value you do not set.
| Field | Type | Notes |
|---|---|---|
| string | Required | |
| wording | string | null | Required |
| ip_address | string | null | Required |
| user_agent | string | null | Required |
| consented_at | integer | Required |
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/consents" \
-H "Authorization: Bearer $SENDOCADO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "maya@example.com",
"wording": "Yes, send me product updates from Acme.",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6)",
"consented_at": 1789999940000
}'{
"id": "ddlz2uhfkvml73ctyxv2kgafrfw0h9ny",
"email": "maya@example.com",
"source": "api",
"wording": "Yes, send me product updates from Acme.",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6)",
"consented_at": 1789999940000,
"created_at": 1790000000000
}Errors
| Status | Type | Example message |
|---|---|---|
| 400 | invalid_data | Send a JSON request body. |
| 401 | unauthorized | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | project_not_found | Project not found. |
| 429 | rate_limited | Too many requests. Try again in 12 seconds. |
List unsubscribed and blocked addresses
GET/projects/{project_id}/blocked-emails
One reason per request, newest first. is_still_blocked is false once a later unblock or resubscribe cleared the row; email is null after an erasure.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| project_id | path | string | Required |
| reason | query | "unsubscribed" | "bounced" | "complained" | "manual" | Required |
| cursor | query | string | null | Optional |
| limit | query | integer | Optional, default 50 |
curl "https://sendocado.com/api/v1/projects/$PROJECT_ID/blocked-emails?reason=bounced" \
-H "Authorization: Bearer $SENDOCADO_API_KEY"{
"blocked_emails": [
{
"id": "wt1fd4mx82mux4b0pzcyc3edqmevxrvc",
"email": "old-address@example.com",
"reason": "bounced",
"detail": "550 5.1.1 The email account that you tried to reach does not exist.",
"is_still_blocked": true,
"contact_id": "eh60kvj50ce9uvw53efr4edt2sywb3wk",
"created_at": 1790000000000
}
],
"next_cursor": null
}Errors
| Status | Type | Example message |
|---|---|---|
| 400 | invalid_data | limit: Too big: expected number to be <=200 |
| 401 | unauthorized | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | project_not_found | Project not found. |
| 429 | rate_limited | Too many requests. Try again in 12 seconds. |