> ## Documentation Index
> Fetch the complete documentation index at: https://docs.senderkit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Send a message

> Enqueue a message for delivery. Provide **either** `template` (send a
stored template) **or** `content` (inline raw content) — never both.
Returns `202` immediately; the message is dispatched asynchronously.




## OpenAPI

````yaml https://www.senderkit.com/openapi.yaml post /v1/send
openapi: 3.1.0
info:
  title: SenderKit Public API
  version: 1.0.0
  description: >
    Public REST API for SenderKit — send transactional messages (email, SMS,

    push, web push), list message history, and read/render templates.


    ## Authentication

    All endpoints require a Bearer API key:

        Authorization: Bearer sk_live_xxx

    The `sk_live_` / `sk_test_` prefix selects the environment (live vs. test).

    The prefix is only a hint for humans; the secret is the full token. Keys are

    created in the dashboard and shown once at creation.


    ## Sends are asynchronous

    `POST /v1/send` enqueues the message and returns `202` with `status:
    "queued"`.

    Delivery happens out of band; poll `GET /v1/messages` to observe progress.


    ## Rate limits

    All endpoints are rate limited per API key. Sends and reads count against

    separate budgets, so listing messages never competes with sending them.

    A `429` response includes a `Retry-After` header (seconds).
servers:
  - url: https://api.senderkit.com
    description: Production
  - url: http://localhost:3000/api
    description: Local development
security:
  - apiKey: []
tags:
  - name: Context
    description: Inspect the workspace and environment an API key operates in.
  - name: Send
    description: Dispatch messages.
  - name: Messages
    description: Read message history and cancel pending sends.
  - name: Templates
    description: Read and render stored templates.
paths:
  /v1/send:
    post:
      tags:
        - Send
      summary: Send a message
      description: |
        Enqueue a message for delivery. Provide **either** `template` (send a
        stored template) **or** `content` (inline raw content) — never both.
        Returns `202` immediately; the message is dispatched asynchronously.
      operationId: sendMessage
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: |
            Optional caller-supplied key to make a send safely retryable. A
            repeat request with the same key (within the same workspace and
            environment) returns the original send instead of creating a
            duplicate. Recommended for all sends so client retries never
            double-send.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/TemplateSend'
                - $ref: '#/components/schemas/RawEmailSend'
                - $ref: '#/components/schemas/RawSmsSend'
                - $ref: '#/components/schemas/RawPushSend'
                - $ref: '#/components/schemas/RawWebPushSend'
            examples:
              template:
                summary: Template-based send
                value:
                  template: welcome
                  to: user@example.com
                  vars:
                    name: Ada
                    dashboardUrl: https://app.example.com/dashboard
                  metadata:
                    userId: usr_123
              rawEmail:
                summary: Raw email send
                value:
                  channel: email
                  to: user@example.com
                  content:
                    subject: Your receipt
                    html: <p>Thanks for your order, {{name}}.</p>
                  interpolate: true
                  vars:
                    name: Ada
      responses:
        '202':
          description: Message accepted and queued for delivery.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendResult'
        '400':
          description: |
            Invalid request — malformed JSON or failed validation
            (`invalid_request`), a channel/template mismatch
            (`channel_mismatch`), an envelope field (cc/bcc/replyTo/attachments)
            used on a non-email channel (`envelope_not_supported`), or (in live
            mode) the provider for the channel is not configured.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: |
            Monthly message quota exhausted for the workspace's plan
            (`message_limit_reached`). Enforcement is soft: sends keep working
            past the advertised plan limit up to a grace buffer (e.g. Free =
            3,000 + 20%); only past that buffer are sends rejected. Live sends
            only — test-mode sends never consume quota. Upgrade the plan to
            keep sending. Workspaces sending through SenderKit's managed
            provider can also hit a separate managed-send cap
            (`managed_send_limit_reached`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Template not found, or has no published version in live mode.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: |
            The template exists but is archived (`template_archived`) and can no
            longer be sent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    TemplateSend:
      type: object
      required:
        - template
        - to
      properties:
        template:
          type: string
          description: Template slug to send.
        version:
          type: integer
          minimum: 1
          description: |
            Optional explicit version pin. Defaults to the latest version in
            test mode and the current published version in live mode.
        channel:
          $ref: '#/components/schemas/Channel'
        to:
          type: string
          description: Recipient (email address, phone number, or device token).
        vars:
          type: object
          additionalProperties: true
          description: Variables interpolated into the template.
        metadata:
          $ref: '#/components/schemas/Metadata'
        scheduledAt:
          type: string
          format: date-time
          description: |
            Optional ISO-8601 timestamp to schedule the send for later. Must be
            in the future and within 30 days. When provided, the send is
            accepted with `status: "scheduled"` and dispatched at that time.
        cc:
          type: array
          items:
            type: string
            format: email
          description: Email-only. Cc recipients applied at send time.
        bcc:
          type: array
          items:
            type: string
            format: email
          description: Email-only. Bcc recipients applied at send time.
        replyTo:
          type: string
          format: email
          description: Email-only. Reply-To address applied at send time.
        attachments:
          type: array
          items:
            $ref: '#/components/schemas/Attachment'
          description: Email-only. Per-send attachments — not stored on the template.
        from:
          type: string
          format: email
          description: |
            Email-only. Optional From address override (bare address — put the
            display name in `fromName`). Defaults to the provider connection's
            address. On managed sending it is honored only when the address is
            on the workspace's verified sending domain.
        fromName:
          type: string
          maxLength: 128
          description: |
            Email-only. Optional From display name override, rendered as
            `Name <address>`. Defaults to the provider connection's from name.
            Must not contain control characters or angle brackets.
    RawEmailSend:
      type: object
      required:
        - channel
        - content
        - to
      properties:
        channel:
          type: string
          const: email
        to:
          type: string
        from:
          type: string
          format: email
          description: |
            Optional From address override (bare address — put the display name
            in `fromName`). Defaults to the provider connection's address. On
            managed sending it is honored only when the address is on the
            workspace's verified sending domain.
        fromName:
          type: string
          maxLength: 128
          description: |
            Optional From display name override, rendered as `Name <address>`.
            Defaults to the provider connection's from name. Must not contain
            control characters or angle brackets.
        interpolate:
          type: boolean
          default: false
          description: When true, `vars` are interpolated into the inline content.
        content:
          type: object
          required:
            - subject
            - html
          properties:
            subject:
              type: string
            preheader:
              type: string
            html:
              type: string
            text:
              type: string
            cc:
              type: array
              items:
                type: string
                format: email
            bcc:
              type: array
              items:
                type: string
                format: email
            replyTo:
              type: string
              format: email
            attachments:
              type: array
              items:
                $ref: '#/components/schemas/Attachment'
        vars:
          type: object
          additionalProperties: true
        metadata:
          $ref: '#/components/schemas/Metadata'
        scheduledAt:
          type: string
          format: date-time
          description: |
            Optional ISO-8601 timestamp to schedule the send for later. Must be
            in the future and within 30 days. When provided, the send is
            accepted with `status: "scheduled"` and dispatched at that time.
    RawSmsSend:
      type: object
      required:
        - channel
        - content
        - to
      properties:
        channel:
          type: string
          const: sms
        to:
          type: string
        interpolate:
          type: boolean
          default: false
        content:
          type: object
          required:
            - body
          properties:
            body:
              type: string
        vars:
          type: object
          additionalProperties: true
        metadata:
          $ref: '#/components/schemas/Metadata'
        scheduledAt:
          type: string
          format: date-time
          description: |
            Optional ISO-8601 timestamp to schedule the send for later. Must be
            in the future and within 30 days. When provided, the send is
            accepted with `status: "scheduled"` and dispatched at that time.
    RawPushSend:
      type: object
      required:
        - channel
        - content
        - to
      properties:
        channel:
          type: string
          const: push
        to:
          type: string
        interpolate:
          type: boolean
          default: false
        content:
          type: object
          required:
            - title
            - body
          properties:
            title:
              type: string
            body:
              type: string
            data:
              type: object
              additionalProperties:
                type: string
            badge:
              type: integer
              minimum: 0
            sound:
              type: string
        vars:
          type: object
          additionalProperties: true
        metadata:
          $ref: '#/components/schemas/Metadata'
        scheduledAt:
          type: string
          format: date-time
          description: |
            Optional ISO-8601 timestamp to schedule the send for later. Must be
            in the future and within 30 days. When provided, the send is
            accepted with `status: "scheduled"` and dispatched at that time.
    RawWebPushSend:
      type: object
      required:
        - channel
        - content
        - to
      properties:
        channel:
          type: string
          const: web-push
        to:
          type: string
          description: |
            JSON-serialized browser `PushSubscription` (the object returned by
            `pushManager.subscribe()`), including `endpoint` and `keys`.
        interpolate:
          type: boolean
          default: false
        content:
          type: object
          required:
            - title
            - body
          properties:
            title:
              type: string
            body:
              type: string
            icon:
              type: string
              description: URL of the notification icon.
            clickUrl:
              type: string
              description: URL opened when the notification is clicked.
            data:
              type: object
              additionalProperties:
                type: string
            badge:
              type: integer
              minimum: 0
        vars:
          type: object
          additionalProperties: true
        metadata:
          $ref: '#/components/schemas/Metadata'
        scheduledAt:
          type: string
          format: date-time
          description: |
            Optional ISO-8601 timestamp to schedule the send for later. Must be
            in the future and within 30 days. When provided, the send is
            accepted with `status: "scheduled"` and dispatched at that time.
    SendResult:
      type: object
      required:
        - id
        - status
        - livemode
      properties:
        id:
          type: string
          description: Public message ID (e.g. `msg_...`).
          example: msg_0a1b2c3d4e5f6g7h
        status:
          type: string
          enum:
            - queued
            - scheduled
          description: |
            `queued` for an immediate send, or `scheduled` when `scheduledAt`
            was provided on the request.
        livemode:
          type: boolean
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
              example: invalid_request
            message:
              type: string
            issues:
              type: array
              description: Present on validation failures (Zod issues).
              items:
                type: object
                additionalProperties: true
            limit:
              type: integer
              description: Present on rate-limit errors.
    Channel:
      type: string
      enum:
        - email
        - sms
        - push
        - web-push
    Metadata:
      type: object
      description: Caller-supplied tags/IDs. Values must be string, number, or boolean.
      additionalProperties:
        oneOf:
          - type: string
          - type: number
          - type: boolean
    Attachment:
      type: object
      required:
        - filename
        - contentType
        - content
      properties:
        filename:
          type: string
          maxLength: 255
        contentType:
          type: string
          example: application/pdf
        content:
          type: string
          format: byte
          description: Base64-encoded bytes.
        inline:
          type: boolean
          default: false
          description: When true, attachment is referenced from HTML via `cid:<contentId>`.
        contentId:
          type: string
          description: Required when `inline=true`.
      description: |
        Up to 50 attachments per message, 10 MB total decoded size across all
        attachments. Larger messages are rejected with `attachment_too_large`.
  responses:
    Unauthorized:
      description: Missing, malformed, invalid, or revoked API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: API key with an `sk_live_` or `sk_test_` prefix.

````