ASAP Emails API

Outbound email journal: delivery history and the Resend delivery-event webhook.

Operations 12

GET /v1/emails Serve one page of the org's outbound email journal, newest first #
POST /v1/emails Compose and send an ad-hoc email to one of the org's contacts #
GET /v1/emails/scheduled Serve the org's pending scheduled sends, soonest first #
DELETE /v1/emails/scheduled/{kind}/{id} Call off one pending scheduled send #
GET /v1/emails/templates List the org's saved composition templates #
POST /v1/emails/templates Save a new composition template #
DELETE /v1/emails/templates/{id} Delete a saved template #
PATCH /v1/emails/templates/{id} Replace a saved template's name, subject and body #
POST /v1/emails/webhook/resend `POST /v1/emails/webhook/resend` #
GET /v1/emails/{id} Serve one journal entry together with its delivery timeline #
GET /v1/emails/{id}/preview Serve the stored HTML body as a sandboxed standalone document #
POST /v1/emails/{id}/resend Dispatch a journaled email again, replaying its stored parts #

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/asap-cool:asap-cool-emails-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

asap-cool-emails-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: ASAP Platform Emails API
  description: 'Public HTTP API for ASAP Platform (api.asap.cool).


    All `/v1` responses are wrapped in a standard envelope. Success responses are `{ "data": , "meta": { "request_id", "next_cursor"?, "has_more"? } }` — the schema documented for each operation describes the inner `data` value. Errors are `{ "error": { "code", "message", "field"?, "request_id" } }`. Every response also carries an `X-Request-Id` header matching `meta.request_id` / `error.request_id`.'
  license:
    name: BUSL-1.1
  version: 0.0.0
servers:
- url: https://api.asap.cool
  description: Production
tags:
- name: Emails
  description: 'Outbound email journal: delivery history and the Resend delivery-event webhook.'
paths:
  /v1/emails:
    get:
      tags:
      - Emails
      summary: Serve one page of the org's outbound email journal, newest first
      operationId: listEmails
      parameters:
      - name: limit
        in: query
        description: Page size, 1..=100 (defaults to 50).
        required: false
        schema:
          type:
          - integer
          - 'null'
          format: int32
          minimum: 0
      - name: cursor
        in: query
        description: Opaque cursor from a previous page's `meta.next_cursor`.
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: q
        in: query
        description: Free-text search over the subject and the recipient address.
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: status
        in: query
        description: 'Filter to one lifecycle status (`queued`, `sending`, `sent`,

          `delivered`, `bounced`, `complained`, `failed`).'
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: template
        in: query
        description: Filter to one render template (`quote_sent`, `reminder`, …).
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: contact_id
        in: query
        description: Filter to emails sent to one contact (UUID).
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: document_id
        in: query
        description: Filter to emails about one invoice document (UUID).
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: from
        in: query
        description: Inclusive lower bound on the send date (RFC 3339).
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: to
        in: query
        description: Exclusive upper bound on the send date (RFC 3339).
        required: false
        schema:
          type:
          - string
          - 'null'
      responses:
        '200':
          description: Page of journal entries (newest first).
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EmailMessageView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: Invalid limit, cursor or filter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
    post:
      tags:
      - Emails
      summary: Compose and send an ad-hoc email to one of the org's contacts
      operationId: composeEmail
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ComposeRequest'
        required: true
      responses:
        '202':
          description: Dispatched; a journal entry was opened for it.
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: No such contact or template in this org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: Invalid subject or body, or the contact has no address.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '500':
          description: The render sidecar or the mail transport failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
  /v1/emails/scheduled:
    get:
      tags:
      - Emails
      summary: Serve the org's pending scheduled sends, soonest first
      operationId: listScheduledEmails
      parameters:
      - name: limit
        in: query
        description: How many upcoming sends to return, 1..=500 (defaults to 100).
        required: false
        schema:
          type:
          - integer
          - 'null'
          format: int64
      responses:
        '200':
          description: Pending scheduled sends, soonest first.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ScheduledEmailView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: Invalid limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
  /v1/emails/scheduled/{kind}/{id}:
    delete:
      tags:
      - Emails
      summary: Call off one pending scheduled send
      operationId: cancelScheduledEmail
      parameters:
      - name: kind
        in: path
        description: Source of the schedule, from the listing's `source` field (`invoice_reminder`, `signature_reminder`).
        required: true
        schema:
          type: string
      - name: id
        in: path
        description: Identifier of the scheduled row.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '204':
          description: The send was called off.
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Nothing pending under that id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
  /v1/emails/templates:
    get:
      tags:
      - Emails
      summary: List the org's saved composition templates
      operationId: listEmailTemplates
      responses:
        '200':
          description: The org's templates, newest first.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EmailTemplateView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
    post:
      tags:
      - Emails
      summary: Save a new composition template
      operationId: createEmailTemplate
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmailTemplateRequest'
        required: true
      responses:
        '201':
          description: The saved template.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailTemplateView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '409':
          description: That name is already taken.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: Invalid name, subject, body or variable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
  /v1/emails/templates/{id}:
    delete:
      tags:
      - Emails
      summary: Delete a saved template
      operationId: deleteEmailTemplate
      parameters:
      - name: id
        in: path
        description: Template id.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '204':
          description: Deleted.
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: No such template in this org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
    patch:
      tags:
      - Emails
      summary: Replace a saved template's name, subject and body
      operationId: updateEmailTemplate
      parameters:
      - name: id
        in: path
        description: Template id.
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmailTemplateRequest'
        required: true
      responses:
        '200':
          description: The updated template.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailTemplateView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: No such template in this org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '409':
          description: That name is already taken.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: Invalid name, subject, body or variable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
  /v1/emails/webhook/resend:
    post:
      tags:
      - Emails
      summary: '`POST /v1/emails/webhook/resend`'
      operationId: resendEmailWebhook
      requestBody:
        description: Raw Resend event payload (Svix-signature-verified before parsing).
        content:
          application/json: {}
      responses:
        '200':
          description: Event recorded, ignored, or already seen.
        '400':
          description: Missing or invalid Svix signature.
        '503':
          description: The webhook is not configured on this instance.
  /v1/emails/{id}:
    get:
      tags:
      - Emails
      summary: Serve one journal entry together with its delivery timeline
      operationId: getEmail
      parameters:
      - name: id
        in: path
        description: Journal entry id.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The entry and its delivery events.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailDetailView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: No such entry in this org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
  /v1/emails/{id}/preview:
    get:
      tags:
      - Emails
      summary: Serve the stored HTML body as a sandboxed standalone document
      operationId: previewEmail
      parameters:
      - name: id
        in: path
        description: Journal entry id.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The rendered HTML body, sandboxed.
          content:
            text/html: {}
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: No such entry, or its body was purged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
  /v1/emails/{id}/resend:
    post:
      tags:
      - Emails
      summary: Dispatch a journaled email again, replaying its stored parts
      operationId: resendEmail
      parameters:
      - name: id
        in: path
        description: Journal entry to replay.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '204':
          description: Dispatched; a fresh journal entry was opened for it.
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing emails:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: No such entry, or its body was purged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: The recorded recipient is invalid or suppressed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '500':
          description: The mail transport failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - session: []
components:
  schemas:
    ComposeRequest:
      type: object
      description: Body of `POST /v1/emails`.
      required:
      - contact_id
      properties:
        body_html:
          type:
          - string
          - 'null'
          description: Rich-text body. Required unless `template_id` is set.
        contact_id:
          type: string
          format: uuid
          description: Contact to write to. Their recorded address is the recipient.
        document_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Invoice document whose sealed PDF should ride along.
        subject:
          type:
          - string
          - 'null'
          description: Subject line. Required unless `template_id` is set.
        template_id:
          type:
          - string
          - 'null'
          format: uuid
          description: 'Saved template to start from. When set, `subject`/`body_html` are

            ignored and the template''s own copy is used.'
    EmailDetailView:
      allOf:
      - $ref: '#/components/schemas/EmailMessageView'
        description: The entry itself.
      - type: object
        required:
        - events
        properties:
          events:
            type: array
            items:
              $ref: '#/components/schemas/EmailEventView'
            description: Delivery events, oldest first.
      description: A journal entry plus its delivery timeline.
    EmailTemplateRequest:
      type: object
      description: 'Body of `POST` / `PATCH /v1/emails/templates`.


        Named `Email…` rather than `TemplateRequest`: the signature module already

        exposes a schema under that name, and two components sharing one name

        silently overwrite each other in the generated client.'
      required:
      - name
      - subject
      - body_html
      properties:
        body_html:
          type: string
          description: 'Rich-text body; may carry `{{variable}}` placeholders. Sanitized on

            save — what is stored is not necessarily what was sent.'
        name:
          type: string
          description: Name, unique within the org.
        subject:
          type: string
          description: Subject line; may carry `{{variable}}` placeholders.
    EmailEventView:
      type: object
      description: 'One entry on a message''s delivery timeline.


        The raw provider payload is not exposed: it is kept for re-interpretation

        on our side, and it repeats recipient data the view already carries.'
      required:
      - id
      - kind
      - occurred_at
      properties:
        id:
          type: string
          format: uuid
          description: Event id.
        kind:
          type: string
          description: What happened (`delivered`, `opened`, `bounced`, …).
        occurred_at:
          type: string
          description: When it happened, as reported by the provider, RFC 3339.
    EmailMessageView:
      type: object
      description: 'One journal entry, as served by the list and detail endpoints.


        The rendered bodies are deliberately absent: they are large, they hold

        recipient PII, and a listing has no use for them. `has_body` tells a client

        whether `GET /v1/emails/{id}/preview` will return something.'
      required:
      - id
      - template
      - status
      - to_email
      - subject
      - locale
      - has_body
      - created_at
      properties:
        booking_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Linked calendar booking.
        contact_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Linked contact.
        created_at:
          type: string
          description: When the entry was opened (the listing's sort key), RFC 3339.
        delivered_at:
          type:
          - string
          - 'null'
          description: When the provider confirmed delivery, RFC 3339.
        document_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Linked invoice document.
        envelope_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Linked signature envelope.
        failure_reason:
          type:
          - string
          - 'null'
          description: Why the dispatch failed, when it did.
        has_body:
          type: boolean
          description: 'Whether the stored bodies are still available for preview (they are

            dropped by the retention purge).'
        id:
          type: string
          format: uuid
          description: Journal entry id.
        locale:
          type: string
          description: Interface-chrome locale the message was rendered with.
        provider_message_id:
          type:
          - string
          - 'null'
          description: Provider message id — the support handle for a delivery investigation.
        reply_to:
          type:
          - string
          - 'null'
          description: '`Reply-To`, when the send overrode it.'
        sender_name:
          type:
          - string
          - 'null'
          description: '`From` display name, when the send overrode it.'
        sent_at:
          type:
          - string
          - 'null'
          description: When the provider accepted the message, RFC 3339.
        status:
          type: string
          description: Lifecycle status (`queued`/`sent`/`delivered`/`bounced`/…).
        subject:
          type: string
          description: Rendered subject.
        template:
          type: string
          description: Render-template discriminator (`quote_sent`, `reminder`, …).
        to_email:
          type: string
          description: Recipient address.
    ErrorBody:
      type: object
      description: 'Generic error body returned by API handlers when they fail. `code` is a

        stable machine-readable token; `message` is human-friendly and meant for

        surfacing to end users.'
      required:
      - code
      - message
      properties:
        code:
          type: string
          description: Stable machine token (e.g. `invalid_credentials`).
        field:
          type:
          - string
          - 'null'
          description: Offending request field for validation errors, when known.
        message:
          type: string
          description: Human-readable message.
    ScheduledEmailView:
      type: object
      description: 'An email that has not been sent yet.


        Scheduled sends are **not** journal rows: nothing has been rendered or

        dispatched. They are read from the business tables that hold the schedule

        (`invoice.reminder`, `signature.envelope`), so this view is a projection

        over several sources rather than one table.'
      required:
      - id
      - source
      - kind
      - scheduled_at
      properties:
        contact_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Linked contact.
        document_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Linked invoice document.
        envelope_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Linked signature envelope.
        id:
          type: string
          format: uuid
          description: Identifier of the underlying scheduled row, unique within its `source`.
        kind:
          type: string
          description: 'What will be sent (`before_due`, `overdue_7`, `quote_before_expiry`,

            `signature_reminder`, …).'
        label:
          type:
          - string
          - 'null'
          description: Human label of the subject matter (document number, envelope subject).
        scheduled_at:
          type: string
          description: When it is due to go out, RFC 3339.
        source:
          type: string
          description: 'Which schedule this comes from (`invoice_reminder` — one row per

            occurrence; `signature_reminder` — the envelope''s next occurrence).

            Together with `id`, addresses it for cancellation.'
        to_email:
          type:
          - string
          - 'null'
          description: Intended recipient, when the source knows it.
    EmailTemplateView:
      type: object
      description: A saved composition template.
      required:
      - id
      - name
      - subject
      - body_html
      - created_at
      - updated_at
      properties:
        body_html:
          type: string
          description: Sanitized body, with `{{variable}}` placeholders left in place.
        created_at:
          type: string
          description: Creation timestamp, RFC 3339.
        id:
          type: string
          format: uuid
          description: Template id.
        name:
          type: string
          description: Name, unique within the org.
        subject:
          type: string
          description: Subject line, with `{{variable}}` placeholders left in place.
        updated_at:
          type: string
          description: Last update, RFC 3339.
  securitySchemes:
    api_key:
      type: http
      scheme: bearer
    session:
      type: apiKey
      in: cookie
      name: asap_session