Your DMARC data, in your tools. One key, every endpoint.
Pull domains, reports, senders and issues into your scripts, stage a DNS fix, and get a signed post when something changes. Send the key as a bearer token to https://api.trustyourinbox.com/v1.
Start with one call. The key does the rest.
Mint a key in the app under Settings, Integrations, API keys. It carries a read scope and, if you choose, a write scope, and a write only works while the person who minted it can still edit. Call it from a server; there is no browser CORS.
- Base URL
https://api.trustyourinbox.com/v1- Auth
Authorization: Bearer tyi_api_…Header only, never a cookie. Revoking the key, or the role behind it, takes effect on the next call.- Plan
- Pro and up
- Rate limit
- 120 requests per key per 60 seconds Over the limit you get a
429withRetry-Afterin seconds. Staging DNS fixes is capped separately at five per 24 hours. - Format
- JSON in, JSON out,
Cache-Control: no-store - Idempotency
- Send
Idempotency-Keyon a creating write A retry with the same key replays the first answer. The same key with a different body answersidempotency_key_reused. Keys are kept for 24 hours. - Windows
rangein days, default 30, up to 90Reports takelimitinstead: default 50, up to 200.- Versioning
/v1is the stable contract
One call, one domain. kanbello.xyz, as the API sees it.
List the domains in the workspace. Each one comes back with its verification status, whether it sends mail or is monitored only, and the DNS provider we detected.
curl https://api.trustyourinbox.com/v1/domains \
-H "Authorization: Bearer tyi_api_xxxxxxxxxxxxxxxxxxxxxxxx"{
"domains": [
{
"name": "kanbello.xyz",
"status": "verified",
"purpose": "sending",
"dnsProvider": "cloudflare",
"createdAt": "2026-10-14T09:12:40.000Z"
}
]
}Errors you can switch on. One envelope, stable codes.
Every error is { "error", "message" }. Branch on error; the message is for a person. A 429 also carries retryAfterSec.
unauthorizedProvide a valid API key as a bearer token.forbiddenThis API key does not have permission for that.plan_requiredAPI access requires the Pro plan or higher.not_foundNot found.invalid_requestThe request was invalid.rate_limitedToo many requests. Slow down and retry after the delay.internalSomething went wrong on our end.conflictThe request conflicts with the current state.plan_limitThis action would exceed your plan's limit.aup_blockThis domain does not meet our acceptable use policy.idempotency_in_progressA request with this Idempotency-Key is still being processed. Retry shortly.idempotency_key_reusedThis Idempotency-Key was already used for a different request.workspace_requiredThis endpoint is workspace-scoped. Org-scoped keys must target a client workspace with ?workspace=<id>.org_key_requiredThis endpoint is organization-scoped. Authenticate with an organization API key (minted in the MSP console).workspace_archivedThis workspace is archived, so nothing can change.
Every endpoint. All 56, grouped by what they touch.
Domains3 endpoints
GET/v1/domainsList the domains in the workspace
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200The workspace's domains V1DomainsResponsedomainsobject[]namestringstatus"pending" | "verified" | "paused" | "deleted"purpose"sending" | "monitor_only"dnsProviderstring | nullcreatedAtstring (date-time)
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key429Rate limited
POST/v1/domainsAdd a domain to the workspace
Write key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.Idempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Request body
namestringThe domain to add, e.g. acme.compurpose"sending" | "monitor_only" · optionalDefaults to sending.
Responses
201Domain added (pending verification); body carries the TXT to publish V1AddDomainResponsedomainstringstatusstringpurpose"sending" | "monitor_only"verificationobjectrecordTypestringrecordNamestringrecordValuestring
dnsProviderstring | null
400invalid_request (missing or invalid name)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)409conflict (duplicate) or plan_limit422aup_block (domain fails the acceptable use policy)429Rate limited
POST/v1/domains/{domain}/recheckRe-detect the domain's DNS provider now
Write key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Re-checked (body reports the detected provider + whether it changed) V1RecheckResponsedomainstringdetectedbooleanproviderstringproviderNamestring · optionalPresent only when detected is true.changedboolean
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)404Domain not found in the caller's workspace429Rate limited
Authentication & deliverability7 endpoints
GET/v1/domains/{domain}/complianceThe four mandate compliance tiles for one domain
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Compliance tiles V1ComplianceResponsedomainstringcomplianceobjectgoogleV1ComplianceTileOne mandate tile (Google/Yahoo, Microsoft, BIMI, or TLS) for a domain.microsoftV1ComplianceTileOne mandate tile (Google/Yahoo, Microsoft, BIMI, or TLS) for a domain.bimiV1ComplianceTileOne mandate tile (Google/Yahoo, Microsoft, BIMI, or TLS) for a domain.tlsV1ComplianceTileOne mandate tile (Google/Yahoo, Microsoft, BIMI, or TLS) for a domain.
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
GET/v1/domains/{domain}/enforcement-simulationEnforcement Preview: what a stricter DMARC policy would have done
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Enforcement simulation V1EnforcementSimulationResponsedomainstringwindowDaysintegerpurpose"sending" | "monitor_only"currentobjecthasRecordbooleanpolicystring | nullspstring | nullpctinteger | nulltestModeboolean
fullyEnforcedbooleanalignmentPctinteger | nullAdjusted for unidentified sources, as the dashboard shows it: on a domain at enforcement, unidentified mail that receivers quarantined or rejected is left out. Never raw aligned over total.totalVolumeintegernextStepobjectstepstringreasonstringfixTypestring | nulldaysRemaininginteger | null
scenariosV1EnforcementScenario[]One counterfactual per ladder scenario (the recommended next step first, then the p=reject end state). Empty at p=reject.kind"policy_step" | "bump_pct" | "disable_test_mode"targetPolicy"quarantine" | "reject"effectiveSubdomainPolicy"none" | "quarantine" | "reject"verdict"clear_to_flip" | "fix_senders_first" | "investigate" | "wait" | "already_enforced" | "no_data"summarystringtotalsobjectnewlyAffectedBySegmentV1EnforcementSegmentsalreadyEnforcedBySegmentV1EnforcementSegmentsforwardingWithOverrideintegersenderRiskobject[]senderRiskDroppedintegersubdomainsobject[]subdomainsDroppedintegeroverridesobject[]dailySeriesobject[]Messages the target policy would newly ask receivers to act on, per day, in the same three groups as newlyAffectedBySegment.
watchobject | nullThe domain's ACTIVE post-flip watch (14 days after a detected policy strengthening), or null. The watch reads receiver reports and alerts on regression with a one-click rollback; it never writes DNS on its own.toPolicystringchangeDetectedAtstringwatchUntilstringregressionDetectedAtstring | nullregressionSenderNamesstring[]
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
GET/v1/domains/{domain}/sendersSender breakdown, with alignment adjusted for unidentified sources
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.rangequery · integer · optionalWindow in days (clamped to 90, default 30)
Responses
200Sender breakdown V1SendersResponsedomainstringrangeDaysintegertotalVolumeintegeralignmentPctinteger | nullAdjusted for unidentified sources, as the dashboard shows it: on a domain at enforcement, unidentified mail that receivers quarantined or rejected is left out. Never raw aligned over total.blockedSpoofintegerknownVolumeinteger · optionalPresent only when totalVolume > 0.unknownVolumeinteger · optionalPresent only when totalVolume > 0.distinctSendersinteger · optionalPresent only when totalVolume > 0.sendersV1SenderRow[]ipstringnetworkstring | nullThe organization behind the IP's network (its ASN).volumeintegeralignedPctintegerknownboolean
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
GET/v1/domains/{domain}/protocolRecorded authentication status and evidence for a domain
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Authentication records V1ProtocolResponsedomainstringdmarcobject | nullpolicystringpctinteger | nulltestMode"y" | "n" | nullDMARCbis test flag. With t=y, reject requests quarantine and quarantine requests no enforcement; legacy receivers may ignore the flag.npstring | nullDMARCbis non-existent-subdomain policy.reportsToUsboolean | nullmultiRecordboolean | null
spfobject | nulllookupCountinteger | nullLast recorded DNS lookup count, not proof that recursive SPF evaluation completed.allQualifierstring | nullThe qualifier on the final all mechanism (~, -, +, ?).multiRecordboolean | null
dkimSelectorsintegerCount of found selector records, including records without a usable key measurement.dkimMinKeyBitsinteger | nullSmallest valid measured retained key size, or null when none can be measured. Numeric lengths across algorithms do not rank cryptographic strength.mtaStsstring | nullUsable active MTA-STS mode (testing or enforce). Null can mean inactive, unavailable, invalid or absent; consult observations.dkimMeasuredSelectorsinteger · optionalFound selector records with a valid measured key, excluding revoked and unknown keys.observationsobject · optionaldmarcobjectspfobjectmtaStsobject
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
GET/v1/domains/{domain}/dkim-rotationsDKIM key rotation state for a domain
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200DKIM rotation state V1DkimRotationsResponsedomainstringactiveobject[]Non-terminal rotations, oldest first (the wizard's render order).idstring (uuid)oldSelectorstringnewSelectorstringvendorstring | nullVendor label of the old selector at rotation start; null = long-tail self-hosted.custody"self_hosted" | "esp"state"planned" | "new_key_published" | "new_key_signing" | "old_key_retired"createdAtstring (date-time)newKeyPublishedAtstring (date-time) | nullnewKeySigningAtstring (date-time) | nulloldKeyRetiredAtstring (date-time) | nullruaEvidenceobject | nullRUA-observed signing evidence for the new selector; populated only in state new_key_published (reports lag 1-2 days).
lastCompletedobject | nulloldSelectorstringnewSelectorstringcompletedAtstring (date-time) | null
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
GET/v1/domains/{domain}/subdomainsSubdomains sending as a domain, bucketed
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.rangequery · integer · optionalWindow in whole UTC days, any value from 1 to 90 (larger values are clamped to 90; default 30). The dashboard offers 7, 30 and 90; any other value here counts the same day-snapped way.
Responses
200Subdomain breakdown V1SubdomainsResponsedomainstringrangeDaysintegersubdomainCountintegerDistinct subdomains in the returned rows; the full observed count is coverage.matchedSubdomainCount when supplied.truncatedboolean · optionalWhen supplied, true if more matching reported subdomains exist than the returned rows.coverageobject · optionalWhen supplied, distinguishes the full observed population in the selected period from the highest-volume returned subset. Volumes are reported message counts; bucket counts describe returned rows.matchedSubdomainCountinteger · optionalAll matching reported subdomains before the row limit, when supplied.returnedSubdomainCountinteger · optionalNumber of returned subdomains, when supplied.matchedVolumeinteger · optionalReported message count across all matching subdomains, when supplied.returnedVolumeinteger · optionalReported message count across returned subdomains, when supplied.rowLimitinteger · optionalMaximum number of returned subdomains, when supplied.
knownCountinteger · optionalPresent only when subdomainCount > 0.worthALookCountinteger · optionalPresent only when subdomainCount > 0.nonexistentCountinteger · optionalPresent only when subdomainCount > 0.unrecognizedCountinteger · optionalPresent only when subdomainCount > 0.totalVolumeinteger · optionalPresent only when subdomainCount > 0.nxdomainPolicystring | null · optionalWhat a non-existent subdomain resolves to (np ?? sp ?? p). Present only when subdomainCount > 0.extendedSubdomainCountintegerDistinct subdomains over a wider 90-day lookback (subdomain abuse is bursty).subdomainsV1SubdomainRow[]subdomainstringlabelstringThe relative label (e.g. "marketing" for marketing.acme.com).status"Known" | "Review" | "Not in DNS"bucket"known" | "worth_a_look" | "nonexistent"volumeintegeralignmentPctinteger | nullAdjusted for unidentified sources, as the dashboard shows it. Null when none of the subdomain's own mail can be measured.spoofVolumeintegerblockedSpoofintegerdistinctSendersintegerlastSeenstring (date-time) | nullinDnsboolean | nulltrue = the subdomain resolves; false = NXDOMAIN; null = inconclusive.verdictstringwhatItMeansstring
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
GET/v1/domains/{domain}/spoof-activityUnidentified failures acted on and top sources over a window
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.rangequery · integer · optionalWindow in days (clamped to 90, default 30)
Responses
200Spoof activity V1SpoofActivityResponsedomainstringrangeDaysintegerblockedSpoofintegerblockedSpoofPriorintegertopSourcesV1SpoofSource[]ipstringattemptsintegernetworkstring | nullcountrystring | nulldispositionstringThe outcome from this source's largest individual report row, retained for compatibility. It is not the outcome of most messages after aggregation; use quarantined and rejected for outcome totals.quarantinedintegerMessages from this source receivers reported quarantining in the selected window.rejectedintegerMessages from this source receivers reported rejecting in the selected window.
dailyobject[]daystring (date)UTC report day, oldest first.blockedintegerMessages receivers reported quarantining or rejecting from unidentified, unaligned sources.
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
Reports2 endpoints
GET/v1/domains/{domain}/reportsRecent aggregate (RUA) reports for one domain
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.limitquery · integer · optionalMax reports (clamped to 200, default 50)
Responses
200Recent reports V1DomainReportsResponsedomainstringreportsV1ReportRow[]idstring (uuid)reporterstringThe receiver's name, derived from the authenticated sender of the report (never from the report body).reporterDomainstring | nullThe authenticated reporter domain; null on reports ingested before sender authentication existed.reportingOrgstringThe organization name the report body claims for itself. A claim, not an identity.rangeStartstring (date-time)rangeEndstring (date-time)volumeintegerpassinginteger
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
GET/v1/reports/{id}Drill into one aggregate (RUA) report
Read key · Bearer
Parameters
idpath · string · requiredaggregate-report id (from .../reports; workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Report detail V1ReportDetailResponsereportIdstring (uuid)externalReportIdstringreporterstringThe receiver's name, derived from the authenticated sender of the report (never from the report body).reporterDomainstring | nullThe authenticated reporter domain; null on reports ingested before sender authentication existed.reportingOrgstringThe organization name the report body claims for itself. A claim, not an identity.domainstringrangeStartstring (date-time)rangeEndstring (date-time)outcomeV1ReportOutcomealignedintegerdkimOnlyintegerspfOnlyintegerfailedintegertotalintegeralignmentPctinteger | nullRaw aligned over total, the report's own composition as received.spoofVolumeintegeradjustedPctinteger | nullAlignment after exclusions; null when no denominator remains. Exclusion does not establish authorization.recordCountinteger
failureGroupsV1FailureGroup[]senderstringvendorNamestring | nullipCountintegerfailedVolumeintegerclass"forwarding" | "esp_misconfig" | "spoof_likely" | "unknown"diagnosisstring
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Report not found in the caller's workspace429Rate limited
Issues4 endpoints
GET/v1/issuesOpen issues across the workspace
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Open issues V1IssuesResponseissuesV1Issue[]keystringtypestringThe anomaly type id (the issue's stable machine key).severity"opportunity" | "warn" | "crit"headlinestringwhystringdomainstringfirstSeenAtstring (date-time)impactstring | null
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key429Rate limited
GET/v1/domains/{domain}/issuesOpen issues for one domain
Read key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Open issues for the domain V1DomainIssuesResponsedomainstringissuesV1Issue[]keystringtypestringThe anomaly type id (the issue's stable machine key).severity"opportunity" | "warn" | "crit"headlinestringwhystringdomainstringfirstSeenAtstring (date-time)impactstring | null
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the caller's workspace429Rate limited
POST/v1/domains/{domain}/issues/{type}/dismissDismiss or snooze an open issue
Write key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)typepath · string · requiredthe issue type to hide, e.g. `tls_rpt_missing`workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Request body (optional)
permanentboolean · optionaltrue = dismiss indefinitely; false (default) = snooze 7 days.
Responses
200Issue dismissed or snoozed V1DismissIssueResponsedomainstringtypestringmode"dismiss" | "snooze"dismissedtrue
400invalid_request (unknown issue type)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)404Domain not found in the caller's workspace429Rate limited
POST/v1/domains/{domain}/issues/{type}/restoreRestore a dismissed or snoozed issue
Write key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)typepath · string · requiredthe issue (anomaly) type to restoreworkspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Issue restored V1RestoreIssueResponsedomainstringtypestringrestoredtrue
400invalid_request (unknown issue type)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)404Domain not found in the caller's workspace429Rate limited
DNS fixes3 endpoints
POST/v1/domains/{domain}/dns-fixesStage a one-click DNS fix for an issue
Write key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.Idempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Request body
typestringA one-click-fixable issue type, e.g. `dmarc_np_missing`.
Responses
200Nothing to stage (the fix is already in place)201Fix staged; body carries the change id, record, and apply time V1StageFixResponsedomainstringtypestringstagedtruechangeIdstring (uuid)recordNamestringnewValuestringapplyAtstring (date-time)warningstring · optional- or
domainstringtypestringstagedfalsealreadyAppliedtrue
400invalid_request (not a one-click-fixable type, or invalid input)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)404Domain not found in the caller's workspace409conflict (no provider connected, already pending, etc.)429Rate limited (per key, or the staging cap of 5 changes per 24 hours)
GET/v1/changesStaged and recently applied DNS changes for the workspace
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Staged and recently applied changes V1ChangesResponsechangesV1Change[]idstring (uuid)domainstring | nullfixTypestringrecordNamestringrecordTypestringstatusstringstaged, applied, canceled, etc.stagedAtstring (date-time)applyAtstring (date-time) | nullundoablebooleanTrue for an applied change to an existing record. It can be undone in the app at any time, as long as the record still holds the value we wrote. A change that created a record, or one that is itself an undo, is never undoable (run the fix again instead).undoUntilnullAlways null. An undo has no deadline; read undoable instead.
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key429Rate limited
DELETE/v1/changes/{id}Cancel a staged DNS change
Write key · Bearer
Parameters
idpath · string · requiredthe staged change id (from GET /v1/changes)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Change canceled V1CancelChangeResponsecanceledtruechangeV1Change | nullA staged or recently-applied one-click DNS change. The email cancel secret is never exposed.idstring (uuid)domainstring | nullfixTypestringrecordNamestringrecordTypestringstatusstringstaged, applied, canceled, etc.stagedAtstring (date-time)applyAtstring (date-time) | nullundoablebooleanTrue for an applied change to an existing record. It can be undone in the app at any time, as long as the record still holds the value we wrote. A change that created a record, or one that is itself an undo, is never undoable (run the fix again instead).undoUntilnullAlways null. An undo has no deadline; read undoable instead.
400invalid_request (malformed id)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)404No staged change with that id in the caller's workspace409conflict (the change is no longer staged)429Rate limited
Delivery Diagnosis3 endpoints
GET/v1/diagnosisList Delivery Diagnosis cases
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.limitquery · integer · optional
Responses
200Recent diagnosis cases V1DiagnosisListResponsecasesV1DiagnosisCaseSummary[]idstringcreatedAtstringstatus"received" | "analyzing" | "diagnosed" | "needs_headers" | "refused" | "failed"source"email" | "paste"subjectstring | nulldomainstring | nullcausestring | nullcauseLabelstring | nullconfidencestring | null
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key429Rate limited
POST/v1/diagnosisDiagnose pasted message headers
Write key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Request body
headersstringThe original message's full header block (max 64 KiB).
Responses
201The diagnosed case V1DiagnosisCaseDetailidstringcreatedAtstringstatus"received" | "analyzing" | "diagnosed" | "needs_headers" | "refused" | "failed"source"email" | "paste"artifactKindstring | nulldomainstring | nullsubjectstring | nullforwarderEmailstring | nullreplySentAtstring | nullrefusalReasonstring | nullverdictV1DiagnosisVerdict | nullcause"authentication" | "reputation" | "forwarding" | "transport" | "recipient_issue" | "policy_block" | "unclear"confidencestring | nullcauseLabelstring | nullnarrativestring | nullprovidersAffectedstring[]ruledOutobject[]actionsobject[]bounceStatusstring | nullbounceReplyLinestring | null
evidenceV1DiagnosisEvidenceRow[]sourcestringstatestringstateLabelstringfindingstring
fixobjectchangeIdstring | nullchangeStatusstring | null
watchUntilstring | nullrecoveryConfirmedAtstring | null
400invalid_request (missing or oversized headers)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer) or plan_required409conflict (duplicate submission) or plan_limit (monthly quota)422invalid_request (`needs_headers`, or `foreign_domain` when the headers are not for a verified domain in the workspace; the case is still saved and its id is in the body)429Rate limited (incl. the per-workspace daily cap)
GET/v1/diagnosis/{id}One Delivery Diagnosis case in full
Read key · Bearer
Parameters
idpath · string · requiredthe case id (from GET /v1/diagnosis)workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200The case detail V1DiagnosisCaseDetailidstringcreatedAtstringstatus"received" | "analyzing" | "diagnosed" | "needs_headers" | "refused" | "failed"source"email" | "paste"artifactKindstring | nulldomainstring | nullsubjectstring | nullforwarderEmailstring | nullreplySentAtstring | nullrefusalReasonstring | nullverdictV1DiagnosisVerdict | nullcause"authentication" | "reputation" | "forwarding" | "transport" | "recipient_issue" | "policy_block" | "unclear"confidencestring | nullcauseLabelstring | nullnarrativestring | nullprovidersAffectedstring[]ruledOutobject[]actionsobject[]bounceStatusstring | nullbounceReplyLinestring | null
evidenceV1DiagnosisEvidenceRow[]sourcestringstatestringstateLabelstringfindingstring
fixobjectchangeIdstring | nullchangeStatusstring | null
watchUntilstring | nullrecoveryConfirmedAtstring | null
400invalid_request (malformed id)401Missing or invalid API key404No case with that id in the caller's workspace429Rate limited
Mandate Monitor2 endpoints
GET/v1/domains/{domain}/mandateMandate Monitor status for a domain
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.domainpath · string · required
Responses
200Mandate Monitor status V1MandateStatusResponsedomainstringhasCapabilitybooleanprobeMintedbooleangradedCountintegersteadybooleanchecksV1MandateCheck[]idstringlabelstringsourcestringpassedintegerapplicableintegerworststring | null
streamsV1MandateStream[]idstringfromAddressstringdkimDomainstringdkimSelectorstringclassificationstringclassificationSourcestringmessageCountintegerworstVerdictstring | nulllastSeenAtstring
microsoft515Count30dinteger
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the workspace (or the feature is switched off)429Rate limited
GET/v1/mandate/messagesList probe-graded messages
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.domainquery · string · optionallimitquery · integer · optional
Responses
200Graded messages V1MandateMessagesResponsemessagesV1MandateMessage[]idstringreceivedAtstringdomainstring | nullsubjectstring | nullfromAddressstringverdictstringrulebookVersionintegerchecksobject[]
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key429Rate limited
Lookalike Domains2 endpoints
GET/v1/domains/{domain}/lookalikesLookalike domains found for a domain
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.domainpath · string · required
Responses
200Lookalike domains for the domain V1LookalikeDomainsResponsedomainstringhasCapabilitybooleanLite+ has the permutation scan; Free sees receiver-reported rows only.activeCountintegerregisteredCountintegerignoredCountintegerscanV1LookalikeScanState | nullcandidatesGeneratedintegerlastDnsPassCompletedstring | nulllastCtOkstring | nulllastCtErrorstring | null
activeV1LookalikeRow[]idstring (uuid)namestringThe lookalike in ASCII (punycode) form, the stored identity.displaystringUnicode form for display; equals name for plain ASCII.isIdnbooleanrulestringThe generator rule that produced it (`homoglyph`, `tld_swap`, `keyword_affix`, and so on), or `rua_evidence` for a name a receiver reported.ruleLabelstringstatus"active" | "registered" | "ignored"ignoreReasonstring | nullours, partner or other when ignored.hasNsbooleanhasMxbooleanhasSpfbooleanhasDmarcbooleanparkedbooleanName servers or MX at a parking or for-sale service; registered but not set up to send, never active.ctFirstSeenstring | nullEarliest certificate-transparency cert seen for the name (confirmation only).ruaMessages90dintegerMessages receivers reported in the last 90 days with this name as the envelope or header From.ruaLastSeenstring | nullregistrarstring | nullregisteredAtstring | nullRegistration date per RDAP, when known.abuseEmailstring | nullThe registrar's abuse contact per RDAP, for the evidence pack.firstSeenstringlastSeenstring
registeredV1LookalikeRow[]idstring (uuid)namestringThe lookalike in ASCII (punycode) form, the stored identity.displaystringUnicode form for display; equals name for plain ASCII.isIdnbooleanrulestringThe generator rule that produced it (`homoglyph`, `tld_swap`, `keyword_affix`, and so on), or `rua_evidence` for a name a receiver reported.ruleLabelstringstatus"active" | "registered" | "ignored"ignoreReasonstring | nullours, partner or other when ignored.hasNsbooleanhasMxbooleanhasSpfbooleanhasDmarcbooleanparkedbooleanName servers or MX at a parking or for-sale service; registered but not set up to send, never active.ctFirstSeenstring | nullEarliest certificate-transparency cert seen for the name (confirmation only).ruaMessages90dintegerMessages receivers reported in the last 90 days with this name as the envelope or header From.ruaLastSeenstring | nullregistrarstring | nullregisteredAtstring | nullRegistration date per RDAP, when known.abuseEmailstring | nullThe registrar's abuse contact per RDAP, for the evidence pack.firstSeenstringlastSeenstring
ignoredV1LookalikeRow[]idstring (uuid)namestringThe lookalike in ASCII (punycode) form, the stored identity.displaystringUnicode form for display; equals name for plain ASCII.isIdnbooleanrulestringThe generator rule that produced it (`homoglyph`, `tld_swap`, `keyword_affix`, and so on), or `rua_evidence` for a name a receiver reported.ruleLabelstringstatus"active" | "registered" | "ignored"ignoreReasonstring | nullours, partner or other when ignored.hasNsbooleanhasMxbooleanhasSpfbooleanhasDmarcbooleanparkedbooleanName servers or MX at a parking or for-sale service; registered but not set up to send, never active.ctFirstSeenstring | nullEarliest certificate-transparency cert seen for the name (confirmation only).ruaMessages90dintegerMessages receivers reported in the last 90 days with this name as the envelope or header From.ruaLastSeenstring | nullregistrarstring | nullregisteredAtstring | nullRegistration date per RDAP, when known.abuseEmailstring | nullThe registrar's abuse contact per RDAP, for the evidence pack.firstSeenstringlastSeenstring
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the workspace (or the feature is switched off)429Rate limited
POST/v1/domains/{domain}/lookalikes/{id}/ignoreIgnore a lookalike domain
Write key · Bearer
Parameters
domainpath · string · requireddomain name (workspace-scoped)idpath · string (uuid) · requiredthe lookalike row id from GET /v1/domains/{domain}/lookalikesworkspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.Idempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Request body
reason"ours" | "partner" | "other"
Responses
200Ignored V1IgnoreLookalikeResponsedomainstringidstring (uuid)reason"ours" | "partner" | "other"ignoredboolean
400invalid_request (unknown reason) or workspace_required401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)404Domain or lookalike row not found in the caller's workspace (or the feature is switched off)429Rate limited
Sender Authorization Review1 endpoint
GET/v1/domains/{domain}/sender-reviewSender authorization review for a domain
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.domainpath · string · required
Responses
200The sender authorization review V1SenderReviewResponsedomainstringwindowDaysintegerstatus"ok" | "not_enough_history"The >=90-day history honesty gate's verdict; not_enough_history means findings are suppressed, never guessed.historyDaysinteger | nulltreeResolvedbooleanhasSpfRecordbooleanspfRecordstring | nulltrafficCompletebooleanFalse when the traffic read hit its row cap; unused claims are suppressed then.dkimObservationCompletebooleanFalse when grouped signing observations exceeded the observation cap. Signed counts are then lower bounds; missing observations cannot establish idle keys, and idle-key findings are suppressed.findingsLockedbooleanTrue when the plan does not include the findings. findings and dkimIdle are then null, not empty, so no claim is made.findingsV1SenderReviewFinding[] | nulltype"spf_include_unused" | "spf_include_unrecognized"includestringvendorstring | nullrangeCountinteger
dkimIdleobject[] | nullselectorstring
grantsV1SenderReviewGrant[]keystringStable grant identity, e.g. include:sendgrid.net or ip4:203.0.113.0/24.kind"include" | "redirect" | "ip4" | "ip6" | "a" | "mx" | "exists" | "ptr"qualifier"+" | "-" | "~" | "?"SPF qualifier as written: `+` means pass, `-` fail, `~` softfail, and `?` neutral. An omitted qualifier or a redirect serializes as `+`. Non-pass terms are preserved as SPF terms, not permission to send.valuestringvendorstring | nullVendor name when the include matches the anchor catalog; null = unrecognized (never guessed).rangeCountintegerattributedVolumenumberMessages attributed to this grant's ranges in the window (an IP under two grants credits both).attributedIpsintegerlastAttributedAtstring | nullreviewability"reviewable" | "macro" | "non_enumerable" | "truncated" | "unresolved" | "no_ranges" | "mail_host" | "mail_host_unknown" | "not_include"Why the grant can or cannot be judged for usage; anything but reviewable is never flagged unused.
selectorsV1SenderReviewSelector[]selectorstringvendorstring | nullalgostring | nullkeyBitsinteger | nullsource"auto" | "user"signedVolumenumberMessages reported carrying this selector in the window (pass + fail). A lower bound when dkimObservationComplete is false; zero then cannot establish that the key was idle.
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Domain not found in the workspace429Rate limited
Senders4 endpoints
GET/v1/sendersList your custom senders
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200The workspace's custom senders V1CustomSendersResponsecountintegersendersobject[]senderIdstring (uuid)namestringcategory"email_provider" | "esp_marketing" | "esp_transactional" | "security" | "infrastructure" | "crm" | "other"rangesstring[]iconUrlstring | nullvolume30dnumberlastSeenstring | nullcreatedAtstring
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key403Plan does not include API access429Rate limited
POST/v1/sendersIdentify a sending IP or CIDR range as a named vendor
Write key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.Idempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Request body
ipOrCidrstringAn IP or CIDR, e.g. 198.2.132.1 or 198.2.128.0/18.namestringThe vendor or sender name, e.g. Mailchimp.category"email_provider" | "esp_marketing" | "esp_transactional" | "security" | "infrastructure" | "crm" | "other"
Responses
201Sender identified V1IdentifySenderResponsesenderIdstring (uuid)namestringcategory"email_provider" | "esp_marketing" | "esp_transactional" | "security" | "infrastructure" | "crm" | "other"ipOrCidrstring
400invalid_request (missing name, invalid category or invalid CIDR)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)409conflict (a sender with that name already exists)429Rate limited
PATCH/v1/senders/{id}Update a custom sender
Write key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.idpath · string (uuid) · requiredIdempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Request body
namestring · optionalcategory"email_provider" | "esp_marketing" | "esp_transactional" | "security" | "infrastructure" | "crm" | "other" · optionaladdRangestring · optionalAn IP or CIDR to add (widest /24 IPv4, /48 IPv6; max 16 ranges per sender).removeRangestring · optionalAn existing range to remove. The last range is refused; delete the sender instead.
Responses
200Sender updated V1UpdateSenderResponsesenderIdstring (uuid)namestringcorpusOverlapsobject[]Input ranges that sit inside a global catalog vendor's published ranges (warn-and-allow).rangestringcorpusSenderstring
400invalid_request (nothing to do, invalid category, invalid or too-wide CIDR, or range not found)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer)404Not this workspace's custom sender (catalog vendors are never editable)409conflict (duplicate name or range, overlap with your own sender, range cap, last range)429Rate limited
DELETE/v1/senders/{id}Delete a custom sender
Write key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.idpath · string (uuid) · requiredIdempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Responses
200Sender deleted V1DeleteSenderResponsesenderIdstring (uuid)namestringdeletedboolean
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is not an admin or owner; deleting is reserved for admins and owners)404Not this workspace's custom sender429Rate limited
Webhooks4 endpoints
GET/v1/webhooksList your outbound webhook endpoints
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200The workspace's webhook endpoints V1WebhooksResponsecountintegerwebhooksobject[]idstring (uuid)urlstringdescriptionstring | nullsecretPrefixstringeventTypesstring[] | nullstatus"active" | "disabled"consecutiveFailuresinteger
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key403Plan does not include outbound webhooks429Rate limited
POST/v1/webhooksRegister an outbound webhook endpoint
Write key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.Idempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Request body
urlstringThe https endpoint URL to POST events to.descriptionstring · optionalOptional label, e.g. Prod SIEM.eventTypes("anomaly.crit" | "dns_fix.applied" | "dns_fix.reverted" | "dns_fix.failed" | "diagnosis.completed" | "diagnosis.recovery_confirmed" | "enforcement.regression_detected" | "enforcement.watch_all_clear" | "mandate.gap_detected" | "mandate.gap_resolved")[] · optionalWhich events to receive; omit for all.
Responses
201Endpoint created (signing secret returned once) V1CreateWebhookResponseidstring (uuid)secretstring | nullThe signing secret, returned once and never again. Null on an Idempotency-Key replay (the secret is never persisted for replay).secretPrefixstring
400invalid_request (invalid URL, not https, private address or unknown event type)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is a viewer), or plan_required409conflict (endpoint limit reached)429Rate limited
DELETE/v1/webhooks/{id}Delete an outbound webhook endpoint
Write key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.idpath · string (uuid) · requiredIdempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Responses
200Endpoint deleted V1DeleteWebhookResponseidstring (uuid)deletedboolean
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key403forbidden (a read-only key, or a key whose creator is not an admin or owner; deleting is reserved for admins and owners)404Not this workspace's webhook endpoint429Rate limited
GET/v1/webhooks/{id}/deliveriesRecent delivery attempts for an endpoint
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.idpath · string (uuid) · required
Responses
200Recent delivery attempts V1WebhookDeliveriesResponseendpointIdstring (uuid)countintegerdeliveriesobject[]idstring (uuid)eventTypestringstatus"pending" | "delivered" | "failed"responseCodeinteger | nullerrorstring | nullattemptinteger
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key404Not this workspace's webhook endpoint429Rate limited
Workspace1 endpoint
GET/v1/workspace/healthWorkspace-wide health summary
Read key · Bearer
Parameters
workspacequery · string (uuid) · optionalOrganization keys only (the MSP partner API): the client workspace this call acts on. Ids come from GET /v1/msp/clients, or the Client workspace IDs table on /msp/api. Required with an organization key, which answers 400 workspace_required without it. A workspace outside the key's own organization answers 404, which says nothing about whether it exists. A workspace key ignores this parameter and stays pinned to its own workspace.
Responses
200Workspace health summary V1WorkspaceHealthalignmentPctinteger | nullWorkspace alignment percentage, adjusted for unidentified sources as the dashboard shows it.trendDeltaPpinteger | nullChange in points vs the prior window.totalMessagesintegertotalReportsintegertotalDomainsinteger
400workspace_required (an organization key called without ?workspace=)401Missing or invalid API key429Rate limited
MSP20 endpoints
GET/v1/msp/clientsThe MSP book of business
Organization key with msp:read · Bearer
Responses
200The book of business V1MspClientsResponseclientsV1MspClientRow[]workspaceIdstringnamestringcreatedAtstring (date-time)archivedAtstring (date-time) | nullcontactEmailstring | nulldomainsTotalintegerdomainsVerifiedintegersendingDomainsintegerenforcedSendingDomainsintegeropenCritsintegerblockedSpoof30dintegerdataThroughstring | nullRUA data-through timestamp; null = no reports yet.
archivedV1MspClientRow[]workspaceIdstringnamestringcreatedAtstring (date-time)archivedAtstring (date-time) | nullcontactEmailstring | nulldomainsTotalintegerdomainsVerifiedintegersendingDomainsintegerenforcedSendingDomainsintegeropenCritsintegerblockedSpoof30dintegerdataThroughstring | nullRUA data-through timestamp; null = no reports yet.
summaryobjectclientCountintegerdomainCountintegersendingDomainsintegerenforcedSendingDomainsintegeropenCritsintegerblockedSpoof30dintegerworkspaceLimitinteger | null
401Missing or invalid API key403org_key_required (workspace key) or plan_required429Rate limited
POST/v1/msp/clientsCreate a client workspace
Organization key with msp:write · Bearer
Parameters
Idempotency-Keyheader · string · optionalOptional client key; a retry with the same value replays the original response.
Request body
namestringThe client's display name, e.g. Acme Roofing.
Responses
201Client workspace created V1MspCreateClientResponseworkspaceIdstringnamestringslugstring
400invalid_request (missing or over-long name)401Missing or invalid API key403forbidden (no msp:write, or the creator's role cannot manage the org)409plan_limit (workspace ceiling)429Rate limited
GET/v1/msp/clients/{id}One client workspace's posture
Organization key with msp:read · Bearer
Parameters
idpath · string · requiredthe client workspace id (from GET /v1/msp/clients)
Responses
200The client detail V1MspClientDetailworkspaceIdstringnamestringcreatedAtstring (date-time)archivedAtstring (date-time) | nullinternalbooleancontactEmailstring | nulldomainsobject[]namestringstatusstringpurpose"sending" | "monitor_only"policystring | nullpctinteger | nulltestModebooleanTrue only when the published DMARC record carries `t=y`. A `t=n` record, or one with no `t` tag, is not in test mode.enforcedboolean
membersobject[]emailstringrolestringcreatedAtstring (date-time)
orgLevelMemberCountintegerpendingInvitesobject[]emailstringrolestringexpiresAtstring (date-time)
401Missing or invalid API key403org_key_required or plan_required404No client workspace with that id in the key's organization429Rate limited
POST/v1/msp/clients/{id}/archiveArchive a client
Organization key with msp:write · Bearer
Parameters
idpath · string · required
Responses
200Archived V1MspArchiveResponseworkspaceIdstringarchivedboolean
401Missing or invalid API key403forbidden (scope or role)404Unknown or foreign client id429Rate limited
POST/v1/msp/clients/{id}/restoreRestore an archived client
Organization key with msp:write · Bearer
Parameters
idpath · string · required
Responses
200Restored V1MspArchiveResponseworkspaceIdstringarchivedboolean
401Missing or invalid API key403forbidden (scope or role)404Unknown or foreign client id429Rate limited
POST/v1/msp/clients/{id}/domainsBulk-add domains to a client
Organization key with msp:write · Bearer
Parameters
idpath · string · requiredIdempotency-Keyheader · string · optional
Request body
domainsstring[]Domain names to add.purpose"sending" | "monitor_only" · optionalDefaults to sending.
Responses
200Per-row outcomes V1MspBulkAddResponseaddedintegeritemsobject[]inputstringoutcome"added" | "restored" | "duplicate" | "invalid" | "aup_blocked" | "plan_limit" | "failed"domainstring | null
400invalid_request (empty, over 200 rows, or a non-string entry)401Missing or invalid API key403forbidden (scope or role)404Unknown or foreign client id409conflict (client is archived)429Rate limited
POST/v1/msp/clients/{id}/invitesInvite a client user
Organization key with msp:write · Bearer
Parameters
idpath · string · required
Request body
emailstringrole"viewer" | "member"
Responses
201Invite sent V1MspInviteResponseworkspaceIdstringemailstringrolestring
400invalid_request (email or role)401Missing or invalid API key403forbidden (scope or role)404Unknown or foreign client id409conflict (already a member, already invited, or archived) or plan_limit (seats)429rate_limited (too many requests, or too many invite emails from this organization)
GET/v1/msp/usageA month of the usage ledger
Organization key with msp:read · Bearer
Parameters
monthquery · string · optionalCalendar month, YYYY-MM (UTC). Defaults to the current month.
Responses
200The month's ledger V1MspUsageMonthmonthstringYYYY-MM (UTC calendar month).daysobject[]usageDatestringbillableintegerRetained daily high-water billable quantity.freeMonitorintegernfrinteger
monthPeakBillableintegerMonth high-water including retained event reservations; a projection, not a paid-invoice receipt.perClientSnapshotKnownbooleanWhether the latest monthly per-client snapshot is valid; false distinguishes unavailable history from a genuine empty snapshot.perClientobject[]workspaceIdstringnamestringbillableintegerBillable count in the latest per-client snapshot for this month.freeMonitorintegernfrintegersendinginteger · optionalBillable SENDING domains alone, excluding monitor-only domains that overflowed the free allowance and are being billed. Optional: ledger rows written before 2026-08-17 do not carry it. When present it explains the `billable` figure: `billable - sending` is the overflow, and it is the count the free-monitor allowance anchors to (in the internal workspace, its free domain also counts when it sends).
400invalid_request (malformed month)401Missing or invalid API key403org_key_required or plan_required429Rate limited
GET/v1/msp/brandThe white-label brand profile
Organization key with msp:read · Bearer
Responses
200The brand profile V1MspBrandconfiguredbooleanproductNamestring | nullcolorsobject | nullprimarystring · optionalaccentstring · optional
emailFromNamestring | nullemailFromAddressstring | nullemailDomainStatusstringportalHostnamestring | nullportalHostnameStatusstringskinOwnDashboardbooleansupportUrlstring | nullsupportEmailstring | nullprivacyUrlstring | nulltermsUrlstring | null
401Missing or invalid API key403org_key_required or plan_required429Rate limited
GET/v1/msp/alert-policyThe account alert policy
Organization key with msp:read · Bearer
Responses
200The alert policy V1MspAlertPolicyseverity"crit" | "crit_warn"delivery"instant" | "hourly"newSourcesBlockedbooleanstoredbooleanFalse until the account saved its settings once; the defaults apply until then.updatedAtstring (date-time) | nullclientsobject[]workspaceIdstringnamestringarchivedbooleanoverride"muted" | "crit" | "crit_warn" | nullnull means the account policy applies to this client.
recipientsV1MspAlertRecipient[]idstringaddressstringstate"awaiting" | "confirmed" | "expired" | "unsubscribed"awaiting: the confirmation link was sent and not clicked yet. expired: the link lapsed unclicked. Only a confirmed recipient receives alerts.confirmExpiresAtstring (date-time) | nullconfirmedAtstring (date-time) | nullunsubscribedAtstring (date-time) | nullcreatedAtstring (date-time)
maxRecipientsintegerrouteobjectThe account's own Slack channel and webhook endpoint, for every client.slackobject | nullwebhookobject | null
401Missing or invalid API key403org_key_required or plan_required429Rate limited
PUT/v1/msp/alert-policyReplace the account alert policy
Organization key with msp:write · Bearer
Request body
severity"crit" | "crit_warn"crit alerts on critical findings only; crit_warn adds warnings.delivery"instant" | "hourly"instant emails each alert as it fires; hourly sends one roll-up an hour.newSourcesBlockedbooleanAlert when receivers block a wave of mail from sources new to a domain.
Responses
200Saved V1MspAlertPolicySettingsseverity"crit" | "crit_warn"crit alerts on critical findings only; crit_warn adds warnings.delivery"instant" | "hourly"instant emails each alert as it fires; hourly sends one roll-up an hour.newSourcesBlockedbooleanAlert when receivers block a wave of mail from sources new to a domain.
400invalid_request (a field is missing or not one of its values)401Missing or invalid API key403forbidden (scope or role, or a suspended account)429Rate limited
PUT/v1/msp/alert-policy/clients/{id}Set or clear one client's alert override
Organization key with msp:write · Bearer
Parameters
idpath · string · required
Request body
mode"muted" | "crit" | "crit_warn" | null
Responses
200Saved V1MspAlertOverrideResponseworkspaceIdstringoverride"muted" | "crit" | "crit_warn" | null
400invalid_request (mode missing or not one of its values)401Missing or invalid API key403forbidden (scope or role, or a suspended account turning alerts up)404Unknown or foreign client id429Rate limited
GET/v1/msp/alert-policy/recipientsThe extra alert recipients
Organization key with msp:read · Bearer
Responses
200The recipients V1MspAlertRecipientsrecipientsV1MspAlertRecipient[]idstringaddressstringstate"awaiting" | "confirmed" | "expired" | "unsubscribed"awaiting: the confirmation link was sent and not clicked yet. expired: the link lapsed unclicked. Only a confirmed recipient receives alerts.confirmExpiresAtstring (date-time) | nullconfirmedAtstring (date-time) | nullunsubscribedAtstring (date-time) | nullcreatedAtstring (date-time)
maxRecipientsintegerThe most extra recipients an account may have.
401Missing or invalid API key403org_key_required or plan_required429Rate limited
POST/v1/msp/alert-policy/recipientsAdd an extra alert recipient
Organization key with msp:write · Bearer
Parameters
Idempotency-Keyheader · string · optional
Request body
addressstring
Responses
201Added; the confirmation email was sent V1MspAlertRecipientAddedidstringaddressstringstate"awaiting"
400invalid_request (not a valid email address)401Missing or invalid API key403forbidden (scope or role, or a suspended account)409conflict (already listed, a member, suppressed, unsubscribed, or the list is full)429rate_limited (too many requests, or too many confirmation emails from this account)502email_failed (saved, but the confirmation email did not send; the body carries the id)
DELETE/v1/msp/alert-policy/recipients/{id}Remove an extra alert recipient
Organization key with msp:write · Bearer
Parameters
idpath · string · requiredIdempotency-Keyheader · string · optional
Responses
200Removedidstringremovedboolean
401Missing or invalid API key403forbidden (scope or role)404Unknown or foreign recipient id429Rate limited
POST/v1/msp/alert-policy/recipients/{id}/resendResend a recipient's confirmation link
Organization key with msp:write · Bearer
Parameters
idpath · string · requiredIdempotency-Keyheader · string · optional
Responses
200Sentidstringresentboolean
401Missing or invalid API key403forbidden (scope or role, or a suspended account)404Unknown or foreign recipient id409conflict (the recipient is not waiting to confirm)429rate_limited (too many requests, or too many confirmation emails from this account)502email_failed (the confirmation email did not send)
POST/v1/msp/alert-policy/webhookCreate the account webhook endpoint
Organization key with msp:write · Bearer
Parameters
Idempotency-Keyheader · string · optional
Request body
urlstringdescriptionstring · optionalA label, up to 80 characters.
Responses
201Created; the secret is shown once V1MspAlertWebhookCreatedidstringsecretstring | nullThe signing secret, shown once. A replay with the same Idempotency-Key answers null.secretPrefixstring
400invalid_request (the URL was refused; reason says why)401Missing or invalid API key403forbidden (scope or role, or a suspended account) or plan_required409conflict (the account already has an endpoint)429Rate limited
DELETE/v1/msp/alert-policy/webhookDelete the account webhook endpoint
Organization key with msp:write · Bearer
Parameters
Idempotency-Keyheader · string · optional
Responses
200Deleteddeletedboolean
401Missing or invalid API key403forbidden (scope or role)404The account has no webhook endpoint429Rate limited
POST/v1/msp/alert-policy/webhook/enableTurn the account webhook endpoint on
Organization key with msp:write · Bearer
Responses
200Turned on V1MspAlertWebhookStatusstatus"active" | "disabled"
401Missing or invalid API key403forbidden (scope or role, or a suspended account)404The account has no webhook endpoint429Rate limited
POST/v1/msp/alert-policy/webhook/disableTurn the account webhook endpoint off
Organization key with msp:write · Bearer
Responses
200Turned off V1MspAlertWebhookStatusstatus"active" | "disabled"
401Missing or invalid API key403forbidden (scope or role)404The account has no webhook endpoint429Rate limited
A signed post when something changes. Check it with one HMAC.
Register an endpoint and we post each event as JSON. Every delivery is signed with your endpoint's whsec_ secret, so you can verify it came from us and reject a replay.
- Signature
X-TYI-Signature: sha256=<hex>HMAC-SHA256 overtimestamp.rawBodywith your secret.- Timestamp
X-TYI-TimestampUnix seconds, in its own header, so you can refuse a stale one.- Event id
X-TYI-Event-Id- Plan
- Pro and up
- anomaly.crit
- dns_fix.applied
- dns_fix.reverted
- dns_fix.failed
- diagnosis.completed
- diagnosis.recovery_confirmed
- enforcement.regression_detected
- enforcement.watch_all_clear
- mandate.gap_detected
- mandate.gap_resolved
Or ask an assistant. The same data over MCP.
Claude, Cursor or any MCP client can read your workspace and, with a write token, do what you could do yourself: dismiss an issue, re-check DNS, stage a one-click fix.
- Create a tokenIn the app under Settings, Integrations, MCP. You see it once; store it like a password and revoke it there any time.
- Add the serverURL
https://mcp.trustyourinbox.com, with the token as a Bearer credential. - AskStart with "list my domains". A read token answers; a write token acts, and every change it makes carries the token's name in your audit log.
Start with one domain. Add a key when you need one.
Free for one domain, no card. API keys and webhooks come with the Pro plan.