# Senders and domains

The email provider a project sends through and the domains verified with it.

## Get the sender

`GET /projects/{project_id}/sender`

The provider account the project sends through, without its secrets. 404 sender_not_found until one is connected.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `project_id` | path | string | Required |

```sh Request
curl "https://sendocado.com/api/v1/projects/$PROJECT_ID/sender" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY"
```

### Response

```json 200
{
  "id": "pr16umx1bz99nfd02is5d9ik40vstqqz",
  "project_id": "ujzde8gxd6ncf10epf91dhodzdoc9is0",
  "from_name": "Sam at Acme",
  "from_email": "sam@acme.example",
  "reply_to_email": null,
  "max_sends_per_hour": 1000,
  "provider": {
    "type": "resend"
  },
  "webhook_url": "https://sendocado.com/api/webhooks/resend/pr16umx1bz99nfd02is5d9ik40vstqqz",
  "status": "ready",
  "last_check": {
    "error": null,
    "checked_at": 1789998200000
  },
  "created_at": 1788272000000
}
```

### Errors

| Status | Type | Example message |
| --- | --- | --- |
| 401 | `unauthorized` | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | `sender_not_found` | Sender not found. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |

## Connect or change the sender

`PUT /projects/{project_id}/sender`

Credentials are tested before anything is saved, then Sendocado sets up the provider’s delivery events itself. A null credential keeps the one stored for the same provider. Gmail connects in the app only.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `project_id` | path | string | Required |

### Body

Send JSON with every field.

| Field | Type | Notes |
| --- | --- | --- |
| `from_name` | string \| null | Required |
| `from_email` | string | Required |
| `reply_to_email` | string \| null | Required |
| `max_sends_per_hour` | integer | Required |
| `provider` | object | Required |
| `provider.type` | "resend" | Required, when type is "resend" |
| `provider.api_key` | string \| null | Required, when type is "resend" |
| `provider.type` | "ses" | Required, when type is "ses" |
| `provider.region` | string | Required, when type is "ses" |
| `provider.access_key_id` | string \| null | Required, when type is "ses" |
| `provider.secret_access_key` | string \| null | Required, when type is "ses" |
| `provider.type` | "postmark" | Required, when type is "postmark" |
| `provider.server_token` | string \| null | Required, when type is "postmark" |
| `provider.account_token` | string \| null | Required, when type is "postmark" |
| `provider.message_stream` | string | Required, when type is "postmark" |
| `provider.type` | "sendgrid" | Required, when type is "sendgrid" |
| `provider.api_key` | string \| null | Required, when type is "sendgrid" |
| `provider.type` | "mailgun" | Required, when type is "mailgun" |
| `provider.api_key` | string \| null | Required, when type is "mailgun" |
| `provider.domain` | string | Required, when type is "mailgun" |
| `provider.region` | "us" \| "eu" | Required, when type is "mailgun" |
| `provider.type` | "smtp" | Required, when type is "smtp" |
| `provider.host` | string | Required, when type is "smtp" |
| `provider.port` | integer | Required, when type is "smtp" |
| `provider.is_secure` | boolean | Required, when type is "smtp" |
| `provider.username` | string | Required, when type is "smtp" |
| `provider.password` | string \| null | Required, when type is "smtp" |
| `provider.type` | "gmail" | Required, when type is "gmail" |

```sh Request
curl -X PUT "https://sendocado.com/api/v1/projects/$PROJECT_ID/sender" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from_name": "Sam at Acme",
  "from_email": "sam@acme.example",
  "reply_to_email": null,
  "max_sends_per_hour": 1000,
  "provider": {
    "type": "resend",
    "api_key": "re_9xKp2mQ7_vB4nR8tY1wZ6cF3hJ5sD0aL"
  }
}'
```

### Response

```json 200
{
  "id": "pr16umx1bz99nfd02is5d9ik40vstqqz",
  "project_id": "ujzde8gxd6ncf10epf91dhodzdoc9is0",
  "from_name": "Sam at Acme",
  "from_email": "sam@acme.example",
  "reply_to_email": null,
  "max_sends_per_hour": 1000,
  "provider": {
    "type": "resend"
  },
  "webhook_url": "https://sendocado.com/api/webhooks/resend/pr16umx1bz99nfd02is5d9ik40vstqqz",
  "status": "ready",
  "last_check": {
    "error": null,
    "checked_at": 1790000000000
  },
  "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 | `sender_not_found` | Sender not found. |
| 409 | `conflict` | This campaign has enrollments, so it cannot be deleted. Pause it instead. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |

## Disconnect the sender

`DELETE /projects/{project_id}/sender`

Removes what Sendocado set up in the provider account; the project sends nothing until a sender is connected again.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `project_id` | path | string | Required |

```sh Request
curl -X DELETE "https://sendocado.com/api/v1/projects/$PROJECT_ID/sender" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY"
```

### Response

`204` with no body.

### Errors

| Status | Type | Example message |
| --- | --- | --- |
| 401 | `unauthorized` | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | `sender_not_found` | Sender not found. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |

## Test sender credentials

`POST /projects/{project_id}/sender-checks`

Tests the same body as the sender update with the provider, without saving anything.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `project_id` | path | string | Required |

### Body

Send JSON with every field.

| Field | Type | Notes |
| --- | --- | --- |
| `from_name` | string \| null | Required |
| `from_email` | string | Required |
| `reply_to_email` | string \| null | Required |
| `max_sends_per_hour` | integer | Required |
| `provider` | object | Required |
| `provider.type` | "resend" | Required, when type is "resend" |
| `provider.api_key` | string \| null | Required, when type is "resend" |
| `provider.type` | "ses" | Required, when type is "ses" |
| `provider.region` | string | Required, when type is "ses" |
| `provider.access_key_id` | string \| null | Required, when type is "ses" |
| `provider.secret_access_key` | string \| null | Required, when type is "ses" |
| `provider.type` | "postmark" | Required, when type is "postmark" |
| `provider.server_token` | string \| null | Required, when type is "postmark" |
| `provider.account_token` | string \| null | Required, when type is "postmark" |
| `provider.message_stream` | string | Required, when type is "postmark" |
| `provider.type` | "sendgrid" | Required, when type is "sendgrid" |
| `provider.api_key` | string \| null | Required, when type is "sendgrid" |
| `provider.type` | "mailgun" | Required, when type is "mailgun" |
| `provider.api_key` | string \| null | Required, when type is "mailgun" |
| `provider.domain` | string | Required, when type is "mailgun" |
| `provider.region` | "us" \| "eu" | Required, when type is "mailgun" |
| `provider.type` | "smtp" | Required, when type is "smtp" |
| `provider.host` | string | Required, when type is "smtp" |
| `provider.port` | integer | Required, when type is "smtp" |
| `provider.is_secure` | boolean | Required, when type is "smtp" |
| `provider.username` | string | Required, when type is "smtp" |
| `provider.password` | string \| null | Required, when type is "smtp" |
| `provider.type` | "gmail" | Required, when type is "gmail" |

```sh Request
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/sender-checks" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from_name": "Sam at Acme",
  "from_email": "sam@acme.example",
  "reply_to_email": null,
  "max_sends_per_hour": 1000,
  "provider": {
    "type": "ses",
    "region": "us-east-1",
    "access_key_id": "AKIAIOSFODNN7EXAMPLE",
    "secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  }
}'
```

### Response

```json 200
{
  "is_ok": true,
  "error": null
}
```

### 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 | `sender_not_found` | Sender not found. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |

## List sending domains

`GET /projects/{project_id}/sending-domains`

Every domain verified (or being verified) with the sender, with its DNS records.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `project_id` | path | string | Required |

```sh Request
curl "https://sendocado.com/api/v1/projects/$PROJECT_ID/sending-domains" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY"
```

### Response

```json 200
{
  "sending_domains": [
    {
      "id": "ztj0wyuhvauvzhmasqxezyex1rdrgdsj",
      "project_id": "ujzde8gxd6ncf10epf91dhodzdoc9is0",
      "sender_id": "pr16umx1bz99nfd02is5d9ik40vstqqz",
      "domain": "acme.example",
      "status": "verified",
      "records": [
        {
          "purpose": "dkim",
          "type": "TXT",
          "name": "resend._domainkey.acme.example",
          "value": "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7",
          "priority": null,
          "status": "verified"
        },
        {
          "purpose": "spf",
          "type": "TXT",
          "name": "send.acme.example",
          "value": "v=spf1 include:amazonses.com ~all",
          "priority": null,
          "status": "verified"
        },
        {
          "purpose": "return_path",
          "type": "MX",
          "name": "send.acme.example",
          "value": "feedback-smtp.us-east-1.amazonses.com",
          "priority": 10,
          "status": "verified"
        }
      ],
      "checked_at": 1789999940000,
      "created_at": 1788272000000
    }
  ],
  "next_cursor": null
}
```

### Errors

| Status | Type | Example message |
| --- | --- | --- |
| 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. |

## Add a sending domain

`POST /projects/{project_id}/sending-domains`

Adds the domain at the provider and returns the DNS records to create. Sendocado checks them every few minutes; status becomes verified once the provider sees them.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `project_id` | path | string | Required |

### Body

Send JSON with every field.

| Field | Type | Notes |
| --- | --- | --- |
| `domain` | string | Required |

```sh Request
curl -X POST "https://sendocado.com/api/v1/projects/$PROJECT_ID/sending-domains" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "acme.example"
}'
```

### Response

```json 201
{
  "id": "ztj0wyuhvauvzhmasqxezyex1rdrgdsj",
  "project_id": "ujzde8gxd6ncf10epf91dhodzdoc9is0",
  "sender_id": "pr16umx1bz99nfd02is5d9ik40vstqqz",
  "domain": "acme.example",
  "status": "pending",
  "records": [
    {
      "purpose": "dkim",
      "type": "TXT",
      "name": "resend._domainkey.acme.example",
      "value": "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7",
      "priority": null,
      "status": "pending"
    },
    {
      "purpose": "spf",
      "type": "TXT",
      "name": "send.acme.example",
      "value": "v=spf1 include:amazonses.com ~all",
      "priority": null,
      "status": "pending"
    },
    {
      "purpose": "return_path",
      "type": "MX",
      "name": "send.acme.example",
      "value": "feedback-smtp.us-east-1.amazonses.com",
      "priority": 10,
      "status": "pending"
    }
  ],
  "checked_at": null,
  "created_at": 1788272000000
}
```

### 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. |
| 409 | `conflict` | This campaign has enrollments, so it cannot be deleted. Pause it instead. |
| 422 | `sender_not_ready` | Verify the domain of your from address before you send email. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |

## Get a sending domain

`GET /sending-domains/{sending_domain_id}`

Its status and each DNS record’s status, as last checked.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `sending_domain_id` | path | string | Required |

```sh Request
curl "https://sendocado.com/api/v1/sending-domains/$SENDING_DOMAIN_ID" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY"
```

### Response

```json 200
{
  "id": "ztj0wyuhvauvzhmasqxezyex1rdrgdsj",
  "project_id": "ujzde8gxd6ncf10epf91dhodzdoc9is0",
  "sender_id": "pr16umx1bz99nfd02is5d9ik40vstqqz",
  "domain": "acme.example",
  "status": "verified",
  "records": [
    {
      "purpose": "dkim",
      "type": "TXT",
      "name": "resend._domainkey.acme.example",
      "value": "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7",
      "priority": null,
      "status": "verified"
    },
    {
      "purpose": "spf",
      "type": "TXT",
      "name": "send.acme.example",
      "value": "v=spf1 include:amazonses.com ~all",
      "priority": null,
      "status": "verified"
    },
    {
      "purpose": "return_path",
      "type": "MX",
      "name": "send.acme.example",
      "value": "feedback-smtp.us-east-1.amazonses.com",
      "priority": 10,
      "status": "verified"
    }
  ],
  "checked_at": 1789999940000,
  "created_at": 1788272000000
}
```

### Errors

| Status | Type | Example message |
| --- | --- | --- |
| 401 | `unauthorized` | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | `sending_domain_not_found` | Sending domain not found. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |

## Remove a sending domain

`DELETE /sending-domains/{sending_domain_id}`

Removes it from Sendocado only; the provider account keeps it, because it may send other mail from it.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `sending_domain_id` | path | string | Required |

```sh Request
curl -X DELETE "https://sendocado.com/api/v1/sending-domains/$SENDING_DOMAIN_ID" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY"
```

### Response

`204` with no body.

### Errors

| Status | Type | Example message |
| --- | --- | --- |
| 401 | `unauthorized` | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | `sending_domain_not_found` | Sending domain not found. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |

## Check a domain’s DNS now

`POST /sending-domains/{sending_domain_id}/checks`

Asks the provider to look at the DNS again. The domain comes back as it stands (202); get it again in a few seconds for the result.

### Parameters

| Name | In | Type | Notes |
| --- | --- | --- | --- |
| `sending_domain_id` | path | string | Required |

```sh Request
curl -X POST "https://sendocado.com/api/v1/sending-domains/$SENDING_DOMAIN_ID/checks" \
  -H "Authorization: Bearer $SENDOCADO_API_KEY"
```

### Response

```json 202
{
  "id": "ztj0wyuhvauvzhmasqxezyex1rdrgdsj",
  "project_id": "ujzde8gxd6ncf10epf91dhodzdoc9is0",
  "sender_id": "pr16umx1bz99nfd02is5d9ik40vstqqz",
  "domain": "acme.example",
  "status": "verified",
  "records": [
    {
      "purpose": "dkim",
      "type": "TXT",
      "name": "resend._domainkey.acme.example",
      "value": "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7",
      "priority": null,
      "status": "verified"
    },
    {
      "purpose": "spf",
      "type": "TXT",
      "name": "send.acme.example",
      "value": "v=spf1 include:amazonses.com ~all",
      "priority": null,
      "status": "verified"
    },
    {
      "purpose": "return_path",
      "type": "MX",
      "name": "send.acme.example",
      "value": "feedback-smtp.us-east-1.amazonses.com",
      "priority": 10,
      "status": "verified"
    }
  ],
  "checked_at": 1789999940000,
  "created_at": 1788272000000
}
```

### Errors

| Status | Type | Example message |
| --- | --- | --- |
| 401 | `unauthorized` | Send a valid API key as "Authorization: Bearer sc_…", or sign in. |
| 404 | `sending_domain_not_found` | Sending domain not found. |
| 429 | `rate_limited` | Too many requests. Try again in 12 seconds. |
