e2a API API

e2a — authenticated email gateway for AI agents. v1 contract.

Operations 76

DELETE /v1/account Delete your account (trash by default; permanent=true erases now) #
GET /v1/account Get account: identity + plan limits + usage (whoami) #
GET /v1/account/api-keys List API keys #
POST /v1/account/api-keys Create an API key #
DELETE /v1/account/api-keys/{id} Revoke an API key #
GET /v1/account/export Export your data (GDPR right-of-access) #
GET /v1/account/sending-access/request Get your latest external sending access request (beta) #
POST /v1/account/sending-access/request Request external sending access (beta) #
GET /v1/account/suppressions List suppressed recipient addresses #
DELETE /v1/account/suppressions/{address} Remove an address from the suppression list #
GET /v1/agents List agents #
POST /v1/agents Create an agent #
DELETE /v1/agents/{email} Delete an agent #
GET /v1/agents/{email} Get an agent #
PATCH /v1/agents/{email} Update an agent #
GET /v1/agents/{email}/contacts List an agent's outreach (beta) #
DELETE /v1/agents/{email}/contacts/{address} Un-enrol a contact (beta) #
GET /v1/agents/{email}/contacts/{address} Get one outreach record (beta) #
PUT /v1/agents/{email}/contacts/{address} Enrol or update outreach state (beta) #
GET /v1/agents/{email}/conversations List conversations #
GET /v1/agents/{email}/conversations/{id} Get a conversation #
GET /v1/agents/{email}/messages List messages #
POST /v1/agents/{email}/messages Send a new email #
DELETE /v1/agents/{email}/messages/{id} Delete a message (move to trash) #
GET /v1/agents/{email}/messages/{id} Get a message #
PATCH /v1/agents/{email}/messages/{id} Update a message (labels) #
GET /v1/agents/{email}/messages/{id}/attachments/{index} Get an attachment (metadata + short-lived download URL) #
POST /v1/agents/{email}/messages/{id}/forward Forward a message #
GET /v1/agents/{email}/messages/{id}/lifecycle Get a message's lifecycle (beta) #
POST /v1/agents/{email}/messages/{id}/reply Reply to a message #
POST /v1/agents/{email}/messages/{id}/restore Restore a message from the trash #
GET /v1/agents/{email}/metrics Get an agent's delivery metrics (beta) #
GET /v1/agents/{email}/protection Get an agent's protection config (beta) #
PUT /v1/agents/{email}/protection Replace an agent's protection config (beta) #
POST /v1/agents/{email}/restore Restore an agent from the trash #
GET /v1/agents/{email}/suppressions List an agent's suppressed recipients (beta) #
POST /v1/agents/{email}/suppressions Suppress a recipient for an agent (beta) #
DELETE /v1/agents/{email}/suppressions/{address} Remove an agent recipient suppression (beta) #
POST /v1/agents/{email}/test Send a test email to the agent's own address #
GET /v1/contacts List contacts (beta) #
POST /v1/contacts Create a contact (beta) #
POST /v1/contacts/import Import contacts in bulk (beta) #
DELETE /v1/contacts/imports/{batch_id} Reverse a contact import (beta) #
DELETE /v1/contacts/{address} Delete a contact (beta) #
GET /v1/contacts/{address} Get a contact (beta) #
PATCH /v1/contacts/{address} Update a contact (beta) #
GET /v1/domains List domains #
POST /v1/domains Register a domain #
DELETE /v1/domains/{domain} Delete a domain #
GET /v1/domains/{domain} Get a domain #
POST /v1/domains/{domain}/verify Verify a domain #
GET /v1/events List events #
GET /v1/events/{id} Get an event #
POST /v1/events/{id}/redeliver Redeliver an event #
GET /v1/info Deployment info #
GET /v1/metrics Get account-wide delivery metrics (beta) #
GET /v1/reviews List messages awaiting review (beta) #
GET /v1/reviews/{id} Get a held message (full detail, beta) #
POST /v1/reviews/{id}/approve Approve a held message (beta) #
POST /v1/reviews/{id}/reject Reject a held message (beta) #
GET /v1/starter-templates List starter templates (beta) #
GET /v1/starter-templates/{alias} Get a starter template (beta) #
GET /v1/templates List templates (beta) #
POST /v1/templates Create a template (beta) #
POST /v1/templates/validate Validate template source (beta) #
DELETE /v1/templates/{id} Delete a template (beta) #
GET /v1/templates/{id} Get a template (beta) #
PATCH /v1/templates/{id} Update a template (beta) #
GET /v1/webhooks List webhooks #
POST /v1/webhooks Create a webhook #
DELETE /v1/webhooks/{id} Delete a webhook #
GET /v1/webhooks/{id} Get a webhook #
PATCH /v1/webhooks/{id} Update a webhook #
GET /v1/webhooks/{id}/deliveries List webhook deliveries #
POST /v1/webhooks/{id}/rotate-secret Rotate a webhook signing secret #
POST /v1/webhooks/{id}/test Fire a synthetic event #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/e2a-dev:e2a-api-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

e2a-dev-api-openapi.yml Raw ↑
components:
  headers:
    ETag:
      description: "Opaque STRONG entity-tag validator for the returned representation, currently a quoted 32-hex-character token (e.g. \"9f86d081884c7d65…\"). Treat it as opaque: never parse, derive, or construct one. Store the received value and replay it VERBATIM in a later If-Match to make a write conditional. Any accepted write moves the validator, so a stale value cannot match and the conditional write is rejected with 412 precondition_failed."
      schema:
        type: string
    RateLimitLimit:
      description: Request quota for the current limiter window.
      schema:
        type: integer
    RateLimitRemaining:
      description: Requests remaining in the current limiter window.
      schema:
        type: integer
    RateLimitReset:
      description: Seconds until the current limiter window resets.
      schema:
        type: integer
    ResourceLocation:
      description: "Canonical path-relative URL of the affected resource. Path segments are percent-encoded PER SEGMENT, which legally leaves the sub-delimiters @ + & = : $ unescaped — /v1/contacts/a.partner@fund.vc is an expected value, and so is the fully-escaped /v1/contacts/a.partner%40fund.vc. Neither form is promised, so never string-compare this header against a URL built locally: percent-decode the final path segment to recover the canonical resource key, then compare that."
      schema:
        format: uri-reference
        type: string
    RetryAfter:
      description: Positive integer seconds to wait before retrying a transient 429 or 503 response.
      schema:
        minimum: 1
        type: integer
    XRequestID:
      description: Always-present request correlation id. Error responses echo the same value in error.request_id.
      schema:
        type: string
  schemas:
    APIKeyExportEntry:
      additionalProperties: true
      properties:
        created_at:
          format: date-time
          type: string
        id:
          type: string
        key_prefix:
          type: string
        last_used_at:
          format: date-time
          type: string
        name:
          type: string
        revoked_at:
          format: date-time
          type: string
      required:
        - id
        - name
        - key_prefix
        - created_at
      type: object
      x-stability-level: beta
    APIKeyView:
      additionalProperties: true
      properties:
        agent_email:
          description: Bound inbox email for agent-scoped keys; omitted for account scope.
          type: string
        created_at:
          format: date-time
          type: string
        expires_at:
          format: date-time
          type: string
        id:
          type: string
        key_prefix:
          description: Non-secret visible prefix (e.g. e2a_acct_… / e2a_agt_…).
          type: string
        last_used_at:
          format: date-time
          type: string
        name:
          type: string
        scope:
          description: "account = workspace admin; agent = bound to one inbox. Open set: new values may be added over time, so treat these as strings and tolerate unknown values. Known values: account, agent."
          type: string
      required:
        - id
        - name
        - key_prefix
        - scope
        - created_at
      type: object
    AccountDailyLimit:
      additionalProperties: true
      properties:
        clean_active_days:
          format: int64
          type: integer
        limit:
          format: int64
          type: integer
        resets_at:
          format: date-time
          type: string
        shared_limit:
          format: int64
          type: integer
        shared_used:
          format: int64
          type: integer
        used:
          format: int64
          type: integer
      required:
        - limit
        - used
        - shared_limit
        - shared_used
        - clean_active_days
        - resets_at
      type: object
    AccountMetricsView:
      additionalProperties: true
      properties:
        agents:
          description: Per-agent breakdown, busiest first. Empty unless group_by=agent was requested.
          items:
            $ref: "#/components/schemas/AgentMetricsGroupView"
          type:
            - array
            - "null"
        agents_truncated:
          description: "True when the account has more agents with traffic than the breakdown returned (cap: 200). The account-wide totals above stay complete — only the breakdown is cut."
          type: boolean
        buckets:
          description: Per-day breakdown, oldest first. Empty unless bucket=day was requested. Each bucket's counters sum to the window totals above; its rates do not average to the window rate, because a rate of rates is not the rate.
          items:
            $ref: "#/components/schemas/MetricsBucketView"
          type:
            - array
            - "null"
        counters:
          description: Every reason code observed across the account in the window, ordered by code.
          items:
            $ref: "#/components/schemas/MetricsCounterView"
          type:
            - array
            - "null"
        end:
          description: Exclusive end of the cohort window.
          format: date-time
          type: string
        messages_in_window:
          description: Messages created in the window across every agent this account owns.
          format: int64
          type: integer
        messages_with_lifecycle:
          description: Messages in the window carrying at least one persisted lifecycle observation. Below messages_in_window means part of the window predates the lifecycle ledger, and every counter here undercounts by that difference.
          format: int64
          type: integer
        rates:
          $ref: "#/components/schemas/MetricsRatesView"
          description: Account-wide rates, on the same fixed denominators the per-agent operation uses.
        reconstructed_observations:
          description: Observations derived from durable message state rather than recorded at the boundary. Included in the counts above; reported separately so the inferred share is visible.
          format: int64
          type: integer
        start:
          description: Inclusive start of the cohort window.
          format: date-time
          type: string
        summary:
          $ref: "#/components/schemas/MetricsSummaryView"
          description: Account-wide counters. Always computed across every agent, never from the possibly-truncated agents array.
        webhooks:
          $ref: "#/components/schemas/WebhookMetricsView"
      required:
        - start
        - end
        - messages_in_window
        - messages_with_lifecycle
        - reconstructed_observations
        - summary
        - rates
        - counters
        - agents
        - agents_truncated
        - webhooks
        - buckets
      type: object
      x-stability-level: beta
    AccountUserView:
      additionalProperties: true
      properties:
        email:
          type: string
        id:
          type: string
      required:
        - id
        - email
      type: object
    AccountView:
      additionalProperties: true
      properties:
        agent_email:
          type: string
        daily_limit:
          $ref: "#/components/schemas/AccountDailyLimit"
          description: External-recipient allowance for this UTC day. Used includes pending or uncertain provider submissions. Shared-identity usage is included in total usage and also bounded by shared_limit. Internal recipients (own live agents and verified owner mailbox) do not count. Omitted when this deployment does not enable the account trust ladder.
        deleted_at:
          description: When the account was moved to the trash. Absent for a live account.
          format: date-time
          type: string
        limits:
          $ref: "#/components/schemas/LimitsCapsView"
        plan_code:
          type: string
        purge_after:
          description: When a trashed account becomes eligible for permanent purge. Absent for a live account.
          format: date-time
          type: string
        read_only:
          description: "True while the account is read-only because its sending is paused pending an abuse review: every write is refused with 403 account_read_only, while reads and moving the account to the trash keep working. False otherwise. Absent when the deployment does not report it or its state is unavailable."
          type: boolean
        restored_at:
          description: "When the account was last restored from the trash. Absent if it never was. API keys and domain verification do not survive a trash: keys must be re-created and domains re-verified after a restore."
          format: date-time
          type: string
        scope:
          description: "Credential scope. Open set: new values may be added over time, so treat these as strings and tolerate unknown values. Known values: account, agent."
          type: string
        sending_access:
          $ref: "#/components/schemas/SendingAccessView"
          description: External sending access eligibility (beta). Booleans only; describes what the account may do, not a promise that a given send passes pause, quota, content or domain checks. Omitted when the deployment does not enable external sending access, or when its state is unavailable.
          x-stability-level: beta
        upgrade_url:
          type: string
        usage:
          $ref: "#/components/schemas/LimitsUsageView"
        user:
          $ref: "#/components/schemas/AccountUserView"
      required:
        - user
        - scope
        - plan_code
        - limits
        - usage
        - upgrade_url
      type: object
    AgentIdentity:
      additionalProperties: true
      properties:
        created_at:
          format: date-time
          type: string
        deleted_at:
          format: date-time
          type: string
        domain:
          type: string
        domain_verified:
          type: boolean
        email:
          type: string
        inbound_7d:
          format: int64
          type: integer
        inbound_allowlist:
          items:
            type: string
          type:
            - array
            - "null"
        inbound_policy:
          type: string
        inbound_policy_action:
          type: string
        inbound_scan:
          type: string
        inbound_scan_block_threshold:
          format: double
          type: number
        inbound_scan_review_threshold:
          format: double
          type: number
        inbound_scan_sensitivity:
          type: string
        last_delivery_at:
          format: date-time
          type: string
        name:
          type: string
        on_expiry:
          type: string
        outbound_7d:
          format: int64
          type: integer
        outbound_allowlist:
          items:
            type: string
          type:
            - array
            - "null"
        outbound_policy:
          type: string
        outbound_policy_action:
          type: string
        outbound_scan:
          type: string
        outbound_scan_block_threshold:
          format: double
          type: number
        outbound_scan_review_threshold:
          format: double
          type: number
        outbound_scan_sensitivity:
          type: string
        pending_count:
          format: int64
          type: integer
        public:
          type: boolean
        registered_domain:
          type: string
        suppress_notifications:
          type: boolean
        ttl_seconds:
          format: int64
          type: integer
        user_id:
          type: string
        webhook_status:
          description: "Webhook posture for this agent, derived from the account's webhook subscribers that match it (a webhook with no agent filter matches every agent). Open set; tolerate unknown values. Known values: none (no webhook matches this agent), healthy (an enabled webhook matches and none serving this agent has a terminally-failed delivery in the last 24h), failing (an enabled webhook matches but at least one delivery on a matching enabled webhook terminally failed in the last 24h), disabled (webhooks match but every one is disabled, turned off manually), auto_disabled (webhooks match, every one is disabled, and at least one was auto-disabled by the chronic-failure sweep). Present on enriched surfaces (account export, dashboard agent list); absent where not computed."
          type: string
      required:
        - domain
        - registered_domain
        - email
        - name
        - domain_verified
        - public
        - created_at
        - user_id
        - ttl_seconds
        - on_expiry
        - suppress_notifications
        - inbound_7d
        - outbound_7d
        - pending_count
        - inbound_policy
        - inbound_policy_action
        - outbound_policy
        - outbound_policy_action
        - inbound_scan
        - inbound_scan_review_threshold
        - inbound_scan_block_threshold
        - outbound_scan
        - outbound_scan_review_threshold
        - outbound_scan_block_threshold
        - inbound_scan_sensitivity
        - outbound_scan_sensitivity
      type: object
      x-stability-level: beta
    AgentMetricsGroupView:
      additionalProperties: true
      properties:
        agent_email:
          type: string
        counters:
          description: Every reason code this agent produced in the window, ordered by code.
          items:
            $ref: "#/components/schemas/MetricsCounterView"
          type:
            - array
            - "null"
        messages_in_window:
          format: int64
          type: integer
        messages_with_lifecycle:
          format: int64
          type: integer
        rates:
          $ref: "#/components/schemas/MetricsRatesView"
        reconstructed_observations:
          format: int64
          type: integer
        summary:
          $ref: "#/components/schemas/MetricsSummaryView"
      required:
        - agent_email
        - messages_in_window
        - messages_with_lifecycle
        - reconstructed_observations
        - summary
        - rates
        - counters
      type: object
      x-stability-level: beta
    AgentMetricsView:
      additionalProperties: true
      properties:
        agent_email:
          description: The agent these metrics describe.
          type: string
        counters:
          description: Every reason code observed in the window, ordered by code. Codes with no observations are omitted rather than reported as zero.
          items:
            $ref: "#/components/schemas/MetricsCounterView"
          type:
            - array
            - "null"
        end:
          description: Exclusive end of the cohort window.
          format: date-time
          type: string
        messages_in_window:
          description: Messages created in the window, from the message table itself.
          format: int64
          type: integer
        messages_with_lifecycle:
          description: Messages in the window carrying at least one persisted lifecycle observation. Below messages_in_window means part of the window predates the lifecycle ledger, and every counter here undercounts by that difference.
          format: int64
          type: integer
        rates:
          $ref: "#/components/schemas/MetricsRatesView"
        reconstructed_observations:
          description: Observations in the window derived from durable message state rather than recorded at the boundary. Included in the counts above; reported separately so the inferred share is visible.
          format: int64
          type: integer
        start:
          description: Inclusive start of the cohort window.
          format: date-time
          type: string
        summary:
          $ref: "#/components/schemas/MetricsSummaryView"
      required:
        - agent_email
        - start
        - end
        - messages_in_window
        - messages_with_lifecycle
        - reconstructed_observations
        - summary
        - rates
        - counters
      type: object
      x-stability-level: beta
    AgentSuppressionAddedData:
      additionalProperties: true
      properties:
        address:
          type: string
        agent_email:
          type: string
        source:
          description: "How the suppression was created. Known values: unsubscribe, manual."
          type: string
      required:
        - agent_email
        - address
        - source
      type: object
      x-stability-level: beta
    AgentSuppressionView:
      additionalProperties: true
      properties:
        address:
          type: string
        agent_email:
          type: string
        created_at:
          format: date-time
          type: string
        reason:
          type: string
        source:
          description: "How the block was created. Known values: unsubscribe, manual."
          type: string
      required:
        - agent_email
        - address
        - source
        - created_at
      type: object
      x-stability-level: beta
    AgentView:
      additionalProperties: true
      properties:
        created_at:
          format: date-time
          type: string
        deleted_at:
          description: When the agent was moved to the trash. Omitted for live agents. A trashed agent is restorable until purged — 30 days after deletion by default (deployment-configurable). Live message data is otherwise retained indefinitely.
          format: date-time
          type: string
        domain:
          description: The exact domain in the agent email address.
          type: string
        domain_verified:
          type: boolean
        email:
          type: string
        name:
          type: string
        registered_domain:
          description: The explicitly registered domain identity that authorizes this agent. For an inherited subdomain agent, this is its verified parent domain.
          type: string
      required:
        - domain
        - registered_domain
        - email
        - name
        - domain_verified
        - created_at
      type: object
    ApproveRequest:
      additionalProperties: false
      properties:
        attachments:
          items:
            $ref: "#/components/schemas/Attachment"
          type: array
        bcc:
          description: Override Bcc recipients. The message is limited to 50 recipients across to, cc, and bcc combined. Each recipient string (display name + address combined) is limited to 320 characters.
          items:
            maxLength: 320
            type: string
          type: array
        cc:
          description: Override Cc recipients. The message is limited to 50 recipients across to, cc, and bcc combined. Each recipient string (display name + address combined) is limited to 320 characters.
          items:
            maxLength: 320
            type: string
          type: array
        html:
          maxLength: 1048576
          type: string
        subject:
          maxLength: 2000
          type: string
        text:
          maxLength: 1048576
          type: string
        to:
          description: Override primary recipients. The message is limited to 50 recipients across to, cc, and bcc combined. Each recipient string (display name + address combined) is limited to 320 characters.
          items:
            maxLength: 320
            type: string
          type: array
      type: object
      x-stability-level: beta
    Attachment:
      additionalProperties: false
      properties:
        content_type:
          examples:
            - application/pdf
          type: string
        data:
          description: Base64-encoded file content. Each attachment must be ≤ 10 MiB decoded; a message may carry at most 10 attachments totaling ≤ 25 MiB decoded.
          examples:
            - base64-encoded-content
          type: string
        filename:
          description: Attachment filename. Maximum 1024 UTF-8 bytes; longer names are rejected as invalid_attachment.
          examples:
            - report.pdf
          type: string
      required:
        - filename
        - content_type
        - data
      type: object
    AttachmentMetaView:
      additionalProperties: true
      properties:
        content_id:
          description: "Content-ID (angle brackets stripped) for an inline part referenced by a body 'cid:' URL, e.g. an embedded image; the key a cid: reference resolves against. Omitted for non-inline attachments."
          type: string
        content_type:
          type: string
        filename:
          type: string
        index:
          description: Stable 0-based attachment index (document order) — the fetch key for the attachment-bytes endpoint.
          format: int64
          type: integer
        size_bytes:
          description: DECODED attachment payload size in bytes (Content-Transfer-Encoding undone) — the size of the file a download yields, not its encoded size inside the raw MIME.
          format: int64
          type: integer
      required:
        - size_bytes
        - index
      type: object
    AttachmentView:
      additionalProperties: true
      properties:
        content_id:
          description: Content-ID (angle brackets stripped) for an inline part referenced by a body 'cid:' URL, e.g. an embedded image. Omitted for non-inline attachments.
          type: string
        content_type:
          type: string
        data:
          type: string
        download_url:
          type: string
        expires_at:
          format: date-time
          type: string
        filename:
          type: string
        index:
          format: int64
          type: integer
        size_bytes:
          description: DECODED attachment payload size in bytes (Content-Transfer-Encoding undone) — exactly what download_url serves and what the 256 KB inline cap is checked against; not the encoded size inside the raw MIME.
          format: int64
          type: integer
      required:
        - index
        - size_bytes
        - download_url
        - expires_at
      type: object
    Authentication:
      additionalProperties: true
      properties:
        dkim:
          description: One result per DKIM signature, in message header order. An unsigned message carries an empty array.
          items:
            $ref: "#/components/schemas/DKIMResult"
          type: array
        dmarc:
          $ref: "#/components/schemas/DMARCResult"
          description: DMARC evaluation of the RFC 5322 Author Domain against aligned SPF and DKIM evidence.
        spf:
          $ref: "#/components/schemas/SPFResult"
          description: SPF evidence for the SMTP peer and RFC 5321 identity.
      required:
        - spf
        - dkim
        - dmarc
      type: object
    ContactDueContact:
      additionalProperties: true
      properties:
        address:
          description: The contact's canonical address — identical to the payload's top-level address.
          type: string
        display_name:
          description: The contact's display name; empty string when unset.
          type: string
        metadata:
          additionalProperties: {}
          description: The contact's caller-owned metadata, verbatim and uninterpreted. An empty object when the contact carries none.
          type: object
      required:
        - address
        - display_name
        - metadata
      type: object
      x-stability-level: beta
    ContactDueData:
      additionalProperties: true
      properties:
        address:
          description: The contact's canonical address — who the agent owes an action to.
          type: string
        agent_email:
          description: The agent that owns this outreach — its id and address (an agent's id IS its email).
          type: string
        contact:
          $ref: "#/components/schemas/ContactDueContact"
          description: The contact's identity, inlined so an agent-scoped consumer needs no account-wide contact read.
        last_conversation_id:
          description: Conversation id of the most recent exchange with this contact, to continue that thread. Omitted when there is none.
          type: string
        last_outbound_at:
          description: When this agent last sent to the contact. Omitted when it never has.
          format: date-time
          type: string
        next_action_at:
          description: The schedule instant that came due — the value set on the engagement, not the time the sweep observed it.
          format: date-time
          type: string
        outbound_count:
          description: How many messages this agent has sent to the contact since the engagement was created. Computed at claim time, not a stored counter.
          format: int64
          type: integer
        replied:
          description: True when the contact has replied since this agent's first outbound to them.
          type: boolean
        stage:
          description: The caller-owned outreach stage, verbatim. e2a never interprets it; empty when unset.
          type: string
      required:
        - address
        - agent_email
        - contact
        - next_action_at
        - outbound_count
        - replied
        - stage
      type: object
      x-stability-level: beta
    ContactEngagementView:
      additionalProperties: true
      properties:
        address:
          type: string
        agent_email:
          type: string
        contact:
          $ref: "#/components/schemas/EmbeddedContactView"
        created_at:
          format: date-time
          type: string
        first_outbound_at:
          description: Server-owned.
          format: date-time
          type:
            - string
            - "null"
        inbound_count:
          description: DMARC-authenticated inbound messages delivered since enrollment. Spoofed, held, blocked, and pre-enrollment messages are excluded. Server-owned.
          format: int64
          type: integer
        last_conversation_id:
          description: Server-owned.
          type: string
        last_inbound_at:
          description: Server-owned.
          format: date-time
          type:
            - string
            - "null"
        last_outbound_at:
          description: Server-owned. Include it in the due query (last_outbound_before) so a lost client state-write cannot cause a duplicate send.
          format: date-time
          type:
            - string
            - "null"
        metadata:
          additionalProperties: {}
          description: Caller-owned key/value data scoped to this relationship. Opaque to e2a.
          type: object
        next_action_at:
          description: When the caller wants to act next. e2a does not act on it; it drives the outreach query and the contact.due wake-up.
          format: date-time
          type:
            - string
            - "null"
        outbound_count:
          description: Successfully submitted outbound messages since enrollment. Queue failures and pre-enrollment history are excluded. Server-owned.
          format: int64
          type: integer
        replied:
          description: Whether this contact has answered since outreach began — computed as last_inbound_at > first_outbound_at, so it means 'replied to us' rather than 'has ever written'. Server-owned.
          type: boolean
        stage:
          description: Caller-defined outreach stage. Opaque to e2a — there is no server-side state machine, and any string is valid.
          type: string
        suppressed:
          description: Whether sends to this address are blocked, by an account-wide or agent-scoped suppression. Server-owned; reflects the same state the send path enforces.
          type: boolean
        suppression_reason:
          description: Free-text detail recorded with the block, when present.
          type: string
        suppression_source:
          description: "Why the block exists, when suppressed. Open set; tolerate unknown values. Known values: unsubscribe, manual, bounce, complaint."
          type: string
        updated_at:
          format: date-time
          type: string
      required:
        - agent_email
        - address
        - stage
        - next_action_at
        - metadata
        - replied
        - suppressed
        - first_outbound_at
        - last_outbound_at
        - last_inbound_at
        - outbound_count
        - inbound_count
        - contact
        - created_at
        - updated_at
      type: object
      x-stability-level: beta
    ContactImportItemResult:
      additionalProperties: true
      properties:
        address:
          description: Canonicalized address. Absent when the row could not be parsed.
          type: string
        code:
          description: "Machine-branchable reason, present for skipped and failed rows. Known values: duplicate_in_batch, already_exists, invalid_recipient, invalid_request, contact_limit_reached."
          type: string
        enrolled:
          description: True when this import created the optional per-agent outreach enrolment for this row. False means no agent was requested or the enrolment already existed.
          type: boolean
        index:
          description: Zero-based position of this row in the submitted contacts array.
          format: int64
          type: integer
        message:
          description: Human-readable explanation for a skipped or failed row.
          type: string
        status:
          description: "Outcome for this row. Known values: created, updated, skipped, failed."
          type: string
        suppressed:
          description: True when this address is on a suppression list. The contact is still recorded — suppression is surfaced here so the import count stays honest rather than silently smaller — but sends to it will be refused.
          type: boolean
      required:
        - index
        - status
      type: object
      x-stability-level: beta
    ContactImportResult:
      additionalProperties: true
      properties:
        batch_id:
          description: Identifier for this import. Pass it to DELETE /v1/contacts/imports/{batch_id} to reverse the upload, or as a filter on the contacts list.
          type: string
        created:
          format: int64
          type: integer
        failed:
          format: int64
          type: integer
        results:
          description: One entry per submitted row, in request order.
          items:
            $ref: "#/components/schemas/ContactImportItemResult"
          type: array
        skipped:
          format: int64
          type: integer
        updated:
          format: int64
          type: integer
      required:
        - batch_id
        - created
        - updated
        - skipped
        - failed
        - results
      type: object
      x-stability-level: beta
    ContactImportRow:
      additionalProperties: false
      properties:
        address:
          description: Email address. Accepts a bare address or an RFC 5322 mailbox ("A. Partner <partner@fund.vc>"). At most 320 Unicode code points; a longer value fails this row alone with invalid_recipient and does not reject the batch.
          type: string
        display_name:
          description: Optional human-readable name, at most 320 Unicode code points; a longer value fails this row alone with invalid_request and does not reject the batch. Omit it and an existing contact keeps the name it already has — so a narrower re-upload that drops the name column does not erase names. Send an explicit empty string to clear one.
          type: string
        metadata:
          additionalProperties: {}
          description: "Optional flat key/value data owned by the caller and stored verbatim; e2a never interprets it. A row that exceeds a bound fails on its own, without affecting the rest of the batch. Bounds, all enforced (400 invalid_request on violation): at most 50 keys; each key at most 128 bytes; each value must be a string, number, boolean, or null — nested objects and arrays are rejected, never flattened; each string value at most 4096 bytes; the whole object at most 16384 bytes once JSON-encoded. The byte-counted limits are UTF-8 octets, so a non-ASCII key or value reaches its limit sooner than its character count suggests."
          maxProperties: 50
          propertyNames:
            maxLength: 128
          type: object
      required:
        - address
    

# --- truncated at 32 KB (395 KB total) ---
# Full source: https://artifacts.apis.io/e2a-dev/openapi/_original/e2a-dev-api-openapi.yml