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

# send.approved

> Access: public. Send a JSON body; unknown keys are rejected with 400 invalid_request (inputs are strict). Responses may gain fields additively — never remove or repurpose; ignore unknown response fields.



## OpenAPI

````yaml /api-reference/openapi.json post /v1/send.approved
openapi: 3.1.0
info:
  title: Dally API
  version: v1
  description: >-
    PRIVATE (posture 2026-09-01): the MCP connector is Dally's only supported
    surface; this REST surface serves internal harnesses only, and its keys are
    WorkOS-dashboard-issued, never product-issued (CONTEXT.md § Positions).
    Headless Dally v1 — Slack-shape RPC: every operation is POST
    /v1/<family.verb> with a JSON body. Cursor-only pagination ({items,
    has_more, next_cursor}; next_cursor null is the only end signal).
    Unversioned while private (Jim 2026-09-03): a retired name drops in the
    first migration after the deployed release stops naming it; inputs are
    strict, responses are tolerant.
servers:
  - url: https://dally.ai
    description: The production Headless Dally v1 API.
security: []
paths:
  /v1/send.approved:
    post:
      tags:
        - send
      summary: >-
        Send one Instagram DM as the creator, in answer to a person who wrote to
        them: the message the creator just read here and asked to send, sent
        word for word. Inside the 24-hour window since the person's last message
        it goes plain; past that window it goes under Meta's human agent tag,
        which Meta refuses until it grants Dally the Human Agent feature, so a
        conversation nobody has written into for a day cannot be answered here
        yet. The answer's tag says which one Meta took. Call it only for a
        message the creator asked to send in this conversation, and never for a
        rule, a schedule or a message nobody read; automation.arm is the way a
        keyword answers by itself. The creator turns approved sends on in
        Dally's settings, and until they do this is refused.
      description: >-
        Access: public. Send a JSON body; unknown keys are rejected with 400
        invalid_request (inputs are strict). Responses may gain fields
        additively — never remove or repurpose; ignore unknown response fields.
      operationId: send.approved
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                platform_account_id:
                  type: string
                  minLength: 1
                  maxLength: 64
                  description: >-
                    The person's Instagram-scoped id, from the DM you are
                    answering: touchpoints, kind dm, platform_account_id. Never
                    a handle, never a display name.
                text:
                  type: string
                  minLength: 1
                  maxLength: 1000
                  description: >-
                    The message as the creator approved it, at most 1000 Unicode
                    code points. It is sent exactly as written: Dally adds no
                    link and no disclosure.
                request_id:
                  type: string
                  format: uuid
                  pattern: >-
                    ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  description: >-
                    A UUID you mint for this one send. The same id twice sends
                    one message, so carry the same id into a retry and mint a
                    fresh one for a new message.
                connection_id:
                  type: string
                  format: uuid
                  pattern: >-
                    ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  description: >-
                    The Instagram connection to send from. Omit it and Dally
                    sends from the creator's live one, which is what every room
                    with one account has.
              required:
                - platform_account_id
                - text
                - request_id
              additionalProperties: false
            example:
              platform_account_id: '1234567890123456'
              text: Thanks for asking! I film every Tuesday, so next week works.
              request_id: e5e5e5e5-e5e5-4e5e-8e5e-e5e5e5e5e5e5
      responses:
        '200':
          description: Success. request_id is present on every response.
          content:
            application/json:
              schema:
                type: object
                properties:
                  sent:
                    type: object
                    properties:
                      platform_account_id:
                        type: string
                        minLength: 1
                        maxLength: 64
                        description: >-
                          The person the message went to, their Instagram-scoped
                          id.
                      text:
                        type: string
                        description: The message, as it was sent.
                      tag:
                        anyOf:
                          - type: string
                            const: human_agent
                          - type: 'null'
                        description: >-
                          Present when Meta took the message under the human
                          agent tag, which is how a send goes past the 24-hour
                          window since the person's last message, and which Meta
                          refuses until it grants Dally the Human Agent feature;
                          null when it went plain, inside that window, and null
                          when Dally read a taken-over send back from the thread
                          and cannot know which tag it carried. An automation
                          never carries it.
                      sent_at:
                        type: string
                        format: date-time
                        pattern: >-
                          ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
                        description: When Instagram took the message.
                      provider_message_id:
                        type: string
                        description: Instagram's id for the message that was sent.
                      touchpoint_id:
                        type: string
                        format: uuid
                        pattern: >-
                          ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        description: >-
                          The send touchpoint this message was recorded on;
                          touchpoints carries it with kind send.
                      connection_id:
                        type: string
                        format: uuid
                        pattern: >-
                          ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        description: The Instagram connection it was sent from.
                    required:
                      - platform_account_id
                      - text
                      - tag
                      - sent_at
                      - provider_message_id
                      - touchpoint_id
                      - connection_id
                    description: >-
                      One DM Dally sent as the creator because the creator read
                      it here and asked for it.
                  show:
                    type: object
                    properties:
                      kind:
                        type: string
                        const: approved_send
                        description: >-
                          The dally_show kind that renders the message that
                          went.
                    required:
                      - kind
                    description: >-
                      How it is shown: dally_write renders the sent message on
                      this very answer with this whole response as its data, so
                      the creator already sees what went; do not render it
                      again.
                  request_id:
                    type: string
                    minLength: 1
                    description: >-
                      Server-minted id for this request. Present on every
                      response, success and error — quote it when reporting a
                      problem.
                required:
                  - sent
                  - show
                  - request_id
              example:
                sent:
                  platform_account_id: '1234567890123456'
                  text: Thanks for asking! I film every Tuesday, so next week works.
                  tag: null
                  sent_at: '2026-09-19T15:00:05.000Z'
                  provider_message_id: aWdfZG1fOTk5
                  touchpoint_id: 3c1f0b7d-4a52-4e88-b6c3-1d7e9a204f61
                  connection_id: 7c1f0b7d-4a52-4e88-b6c3-1d7e9a204f62
                show:
                  kind: approved_send
                request_id: req_01j8zkq
        default:
          description: >-
            Error — the real HTTP status carries the class; the body is always
            the one error envelope. Codes: invalid_request, stale_cursor,
            query_too_broad, unauthorized, forbidden, no_connected_accounts,
            stale_authority, not_found, precondition_failed, method_not_allowed,
            stale_snapshot, stale_revision, execution_payload_erased,
            exhaustiveness_unavailable, rate_limited, internal_error,
            unavailable, search_unavailable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - bearerAuth: []
        - apiKeyAuth: []
components:
  schemas:
    ErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - invalid_request
                - stale_cursor
                - query_too_broad
                - unauthorized
                - forbidden
                - no_connected_accounts
                - stale_authority
                - not_found
                - precondition_failed
                - method_not_allowed
                - stale_snapshot
                - stale_revision
                - execution_payload_erased
                - exhaustiveness_unavailable
                - rate_limited
                - internal_error
                - unavailable
                - search_unavailable
              description: >-
                The machine-readable error code. The set only grows — treat
                unknown codes by falling back to the HTTP status class.
            message:
              type: string
              description: >-
                A human-readable teaching message: what failed and the exact
                fix, never just the symptom.
            details:
              description: >-
                Optional structured context for this code — e.g.
                estimated_matches and ceiling on query_too_broad.
              type: object
              propertyNames:
                type: string
              additionalProperties: {}
          required:
            - code
            - message
          description: The error object — the one shape every failure wears.
        request_id:
          type: string
          minLength: 1
          description: >-
            Server-minted id for this request. Present on every response,
            success and error — quote it when reporting a problem.
      required:
        - error
        - request_id
      description: The ONE error envelope (API-ERR-001).
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'A WorkOS-dashboard-issued internal API key on Authorization: Bearer.'
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: The same internal API key, accepted on x-api-key.

````