Skip to content
Browse the docs

PATCH /domains/{id}

Turn click or open tracking on, change the tracking subdomain, turn sending and receiving on or off, or choose whether suspect received mail is labeled or held.

Name, region and return-path cannot change.

Request

The same call in curl, Node and Python. A path id in the sample is a placeholder.

curl

curl -sS -X PATCH https://api.agentisend.com/domains/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42 \
  -H "Authorization: Bearer $AGENTISEND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'

Node

import { AgentiSend } from 'agentisend';

const client = new AgentiSend(process.env.AGENTISEND_API_KEY);
const result = await client.request({
  method: 'PATCH',
  path: '/domains/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42',
  body: {},
});

Python

import json, os, urllib.request

url = 'https://api.agentisend.com/domains/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42'
headers = {'Authorization': 'Bearer ' + os.environ['AGENTISEND_API_KEY']}
payload = json.dumps({}).encode()
headers['Content-Type'] = 'application/json'
request = urllib.request.Request(url, data=payload, headers=headers, method='PATCH')
print(urllib.request.urlopen(request).read().decode())

Tag: domains. Generated from openapi.json; the anchor patch-domains-id is the id the console's error fix links point at.

Parameters

  • id path · string · required
  • Idempotency-Key header · string · optional — Makes this call safe to retry. Send the same key with the same body and the original response is replayed instead of the work happening twice. Keys are 1-256 characters and are remembered for 7 days. While the first attempt is still running, a second call with that key returns 409 idempotency_in_flight (Resend calls this concurrent_idempotent_requests); the same key with a different body returns 409 idempotency_payload_mismatch (Resend: invalid_idempotent_request). Resend-Idempotency-Key is accepted as the same header.
  • Resend-Idempotency-Key header · string · optional — Alias of Idempotency-Key. Idempotency-Key wins if both are sent.

Request body

FieldTypeRequiredNotes
capabilitiesobjectnoWhat the domain does: sending (default enabled) and receiving (default disabled), each enabled or disabled. At least one must be enabled.
capabilities.receiving"enabled" | "disabled"noenabled or disabled. Default disabled. Enabled: this domain accepts inbound mail.
capabilities.sending"enabled" | "disabled"noenabled or disabled. Default enabled. Disabled: mail from this domain is refused and the domain is not signed for.
click_trackingbooleannoRewrite links so clicks can be counted. Off unless you set this.
custom_return_pathanynoThe return-path label cannot be changed after the domain is created.
inbound_hold_mode"monitor" | "hold"nomonitor delivers every received message and labels it. hold keeps a message labeled review until a person releases it; the arrival webhook waits.
inbound_policy"open" | "allowlist"noopen stores every sender a block rule does not match. allowlist stores a sender only when the envelope or the From header matches an allow rule. A block rule refuses either way.
nameanynoThe domain name cannot be changed. Create another domain.
open_trackingbooleannoCount opens. Off unless you set this.
regionanynoThe region cannot be changed after the domain is created.
return_pathanynoThe return-path label cannot be changed after the domain is created.
tls"opportunistic" | "enforced"noSame as tls_mode. tls_mode wins if both are sent.
tls_mode"opportunistic" | "enforced"noopportunistic tries TLS and falls back. enforced refuses a receiver that cannot do TLS.
tracking_subdomainstring | nullnoChange the tracking host label. Null is refused: the host can be renamed, not removed.

Responses

  • 200 — Success
  • 400 — The request is malformed or a field failed validation. Codes: validation_error, invalid_idempotency_key, invalid_cursor, plan_not_purchasable, stripe_signature_invalid, support_upload_rejected, appsumo_license_not_confirmed, appsumo_ticket_invalid, sign_in_check_required.
  • 401 — No usable credential was sent. Codes: agent_signup_code_rejected, missing_api_key, session_required, mfa_required, appsumo_signature_invalid.
  • 403 — The credential may not do this, or the account or key is stopped. Codes: sending_domain_blocked, young_domain_held, domain_sending_disabled, inbound_sender_refused, domain_not_verified, onboarding_recipient_not_a_member, onboarding_sender_unavailable, account_suspended, account_sandboxed, agent_unconfirmed, recipient_ramp, human_action_required, csrf_origin_rejected, charge_not_this_account, invalid_api_key, restricted_api_key, insufficient_role, invite_email_mismatch, domain_scope_violation, dedicated_ip_assigned_by_us, approval_required, staff_review_required, trust_paused, kill_switch_active.
  • 404 — Nothing with this id exists on the account. Codes: billing_customer_missing, not_found, session_expired, support_ticket_not_found.
  • 409 — The request conflicts with the current state. Codes: domain_already_exists, template_name_taken, template_alias_taken, template_in_use, segment_in_use, contact_resubscribe_required, email_still_scheduled, domain_verified_elsewhere, domain_not_claimable, return_path_subdomain_in_use, overage_not_on_plan, term_not_on_sale, subscription_active, portal_change_unavailable, already_in_account, idempotency_in_flight, idempotency_payload_mismatch, approval_expired, support_closed, support_reopen_expired, support_merge_conflict, support_reply_too_soon, appsumo_license_linked, appsumo_license_inactive, lifetime_sold_on_appsumo.
  • 413 — The request body is larger than this operation accepts. Codes: payload_too_large, support_upload_too_large.
  • 415 — The request body is not JSON. Codes: unsupported_media_type.
  • 422 — A field is well formed but was refused. Codes: missing_required_field, invalid_from_address, invalid_parameter, invalid_attachment, invalid_region, tracking_subdomain_unverified, tracking_subdomain_cannot_be_removed, domain_field_immutable, open_tracking_on_transactional, header_replaced, html_clipped_by_gmail, link_domain_listed, domain_blocklisted, mailbox_provider_domain, onboarding_shape_refused, dkim_key_mismatch, domain_check_window_expired, spf_conflict, spf_lookup_limit, review_sandbox_recipient_only, suppressed_recipient, agent_signup_refused, content_refused, recipient_blocklisted, last_owner_required, seat_limit_reached, invite_not_valid, key_budget_exceeds_plan, domain_limit_reached, webhook_endpoint_limit_reached.
  • 429 — A rate, quota or ramp ceiling was reached. Codes: onboarding_daily_cap_reached, trust_throttled, invite_limit_reached, rate_ceiling_exceeded, daily_quota_exceeded, monthly_quota_exceeded, rate_limit_exceeded, support_rate_limited, appsumo_lookup_limited.
  • 500 — Something failed on our side. Codes: internal_server_error.

Response headers

  • ratelimit-limit — Messages this API key may spend in one 60-second window.
  • ratelimit-remaining — Messages left in the current window.
  • ratelimit-reset — Seconds until the current window resets and the budget refills.

200 body

FieldTypeRequiredNotes
capabilitiesobjectyes
capabilities.receiving"enabled" | "disabled"yes
capabilities.sending"enabled" | "disabled"yes
click_trackingbooleanyes
created_atstringyes
dkim_selectorstringyes
idstringyes
inbound_hold_mode"monitor" | "hold"yes
inbound_policy"open" | "allowlist"yes
inbound_refused_countintegeryes
namestringyes
objectstringyes
open_trackingbooleanyes
provider_hintsobjectyes
provider_hints.cloudflarestringyes
provider_hints.godaddystringyes
provider_hints.namecheapstringyes
provider_hints.route53stringyes
recent_eventsarray<object>yes
recent_events[].detailstringyes
recent_events[].idstringyes
recent_events[].occurred_atstringyes
recent_events[].type"domain.added" | "domain.first_check" | "domain.record_seen" | "domain.record_lost" | "domain.verified" | "domain.demoted" | "domain.window_closed" | "domain.deleted"yes
recordsarray<object>yes
records[].checked_atstring | nullyes
records[].fixstringyes
records[].hoststringyes
records[].namestringyes
records[].name_relativestringyes
records[].observedstring | nullyes
records[].priorityinteger | nullyes
records[].reason"ok" | "not_found" | "unreachable" | "mismatch" | "not_published" | nullyes
records[].record"SPF" | "DKIM" | "DMARC" | "RETURN_PATH" | "TRACKING" | "RECEIVING" | "TRACKING_CAA"yes
records[].requiredbooleanyes
records[].status"pending" | "verified" | "failed" | "recommended"yes
records[].ttlintegeryes
records[].type"TXT" | "MX" | "CNAME" | "CAA"yes
records[].valuestringyes
records[].value_bindstringyes
records[].value_stringsarray<string>yes
region"us" | "eu"yes
return_path_subdomainstringyes
status"pending" | "verified" | "failed" | "partially_verified" | "partially_failed"yes
tls_mode"opportunistic" | "enforced"yes
tracking_issuesarray<object>yes
tracking_issues[].fixstringyes
tracking_issues[].messagestringyes
tracking_issues[].record"TRACKING" | "TRACKING_CAA"yes
tracking_subdomainstringyes
verified_atstring | nullyes
warningsarray<object>yes
warnings[].code"spf_conflict" | "spf_lookup_limit" | "legacy_region"yes
warnings[].fixstringyes
warnings[].mergedstring | nullyes
warnings[].messagestringyes
warnings[].observedstringyes
zonestringyes

Response example

200
{
  "capabilities": {
    "receiving": "enabled",
    "sending": "enabled"
  },
  "click_tracking": false,
  "created_at": "2026-09-04T09:14:00.000Z",
  "dkim_selector": "example",
  "id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
  "inbound_hold_mode": "monitor",
  "inbound_policy": "open",
  "inbound_refused_count": -9007199254740991,
  "name": "example",
  "object": "domain",
  "open_tracking": false,
  "provider_hints": {
    "cloudflare": "example",
    "godaddy": "example",
    "namecheap": "example",
    "route53": "example"
  },
  "recent_events": [],
  "records": [],
  "region": "us",
  "return_path_subdomain": "example",
  "status": "pending",
  "tls_mode": "opportunistic",
  "tracking_issues": [],
  "tracking_subdomain": "example",
  "verified_at": "2026-09-04T09:14:00.000Z",
  "warnings": [],
  "zone": "example"
}

Errors

The codes this operation can answer with, and what to do about each. Every one arrives with its code, message, fix and docs_url.

  • csrf_origin_rejected (403) — This request came from a page on another site, and it changes data. Fix: Call the API with an API key (Authorization: Bearer …) instead of a session cookie, or make the request from the console. Create a key in the console under Settings, API keys.
  • domain_field_immutable (422) — Name, region, and return-path cannot change on an existing domain. Fix: Add a new domain with POST /domains. PATCH /domains/:id accepts click_tracking, open_tracking, tracking_subdomain, and tls_mode only.
  • idempotency_in_flight (409) — A request with this Idempotency-Key is still in progress. Fix: Wait and retry with the same Idempotency-Key to receive the original response.
  • idempotency_payload_mismatch (409) — Same Idempotency-Key was used with a different payload. Fix: Reuse the exact same body for retries, or send a new Idempotency-Key for a new request.
  • insufficient_role (403) — Your role on this account cannot make this change. Fix: Ask an owner to make the change, or to raise your role with PATCH /team/members/:id.
  • internal_server_error (500, internal_server_error) — Unexpected error. Fix: Retry ONCE after a short pause, with the same Idempotency-Key so the retry cannot double-send. If it fails again, stop retrying and report the x-request-id from the response — that id is what identifies this exact failure in support.
  • invalid_api_key (403, invalid_api_key) — API key is invalid or revoked. Fix: Create a new key with POST /api-keys; deleted keys cannot be restored.
  • invalid_idempotency_key (400) — Idempotency-Key must be 1-256 characters. Fix: Send a non-empty Idempotency-Key header of at most 256 characters.
  • invalid_parameter (422) — A parameter has an invalid value. Fix: Correct the named parameter and retry.
  • mfa_required (401) — This session has not completed two-factor authentication. Fix: Finish signing in at https://console.agentisend.com/verify with a code from your authenticator app, or one of your recovery codes. Manage the second factor in the console under Settings, Security.
  • missing_api_key (401, missing_api_key) — Missing API key in authorization header. Fix: Create an API key at https://console.agentisend.com/api-keys and send "Authorization: Bearer as_...". MCP clients can connect with OAuth instead of a key.
  • not_found (404, not_found) — Endpoint or resource does not exist. Fix: List that resource on this account and use an id from the list. For a domain, GET /domains accepts the id or the domain name. A path that is not a route is a typo in the URL.
  • payload_too_large (413) — Request body is larger than this endpoint accepts. Fix: Send a smaller body. Most endpoints accept 1 MB. Sends (POST /emails, /emails/batch, replies) and template, broadcast and automation edits accept 50 MB, which fits 40 MB of attachments after base64. /mcp accepts 1 MB per JSON-RPC call.
  • rate_limit_exceeded (429, rate_ceiling_exceeded, rate_limit_exceeded) — Too many requests. Fix: Back off and retry honoring the Retry-After header.
  • restricted_api_key (403, restricted_api_key) — This API key is restricted to sending only. Fix: Use a full_access key (POST /api-keys with permission=full_access) for management endpoints.
  • tracking_subdomain_cannot_be_removed (422) — The tracking subdomain can be changed, never removed. Fix: Pass a new label such as "clicks" in PATCH /domains/:id. To stop counting opens and clicks, turn those switches off instead.
  • unsupported_media_type (415) — This endpoint does not accept that content type. Fix: Send the body as JSON with `Content-Type: application/json`.
  • validation_error (400, domain_not_verified, invalid_parameter, validation_error) — Error in one or more fields. Fix: Correct the fields listed in the error details and retry the request.