openapi: 3.1.0
info:
  title: Outreach Actions API — Headless AI SDR (Reply.io)
  version: "3.0"
  description: >
    One-off outreach sends outside a sequence — a single email, LinkedIn
    connection request, InMail, message, or voice message, on demand.
    Summary specification for AI agents; the authoritative, complete reference is
    https://docs.reply.io/api-reference/direct-outreach/send-a-direct-email-to-a-contact
    and the full platform OpenAPI at https://docs.reply.io/api-reference/bundled.yaml.
  contact:
    name: Reply.io
    url: https://reply.io
servers:
  - url: https://api.reply.io/v3
security:
  - bearerAuth: []
tags:
  - name: Direct Outreach
paths:
  /contacts/{contactId}/send-direct-email:
    post:
      tags: [Direct Outreach]
      operationId: sendDirectEmail
      summary: Send a direct email to a contact
      description: >
        Sends a one-off email directly to a contact outside of any sequence.
        Requires the `contacts:operate` scope.
      parameters:
        - $ref: "#/components/parameters/contactId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [subject, body]
              properties:
                emailAccountId:
                  type: integer
                  description: >
                    Email account ID to send from. If not provided, uses the
                    default account.
                subject:
                  type: string
                  description: Email subject line.
                body:
                  type: string
                  description: Email body (HTML).
      responses:
        "200":
          description: Email sent successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendDirectEmailResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
  /contacts/{contactId}/send-direct-linkedin-connect:
    post:
      tags: [Direct Outreach]
      operationId: sendDirectLinkedInConnect
      summary: Send a LinkedIn connection request to a contact
      description: >
        Sends a direct LinkedIn connection request outside of any sequence.
        The contact must have a LinkedIn profile URL set; the caller must own
        an active LinkedIn account. Requires the `contacts:operate` scope.
      parameters:
        - $ref: "#/components/parameters/contactId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [linkedInAccountId]
              properties:
                linkedInAccountId:
                  type: integer
                  description: ID of the LinkedIn account to send from.
                message:
                  type: string
                  description: Optional personalized note to include with the request.
      responses:
        "200":
          description: Connection request dispatched.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendDirectLinkedInResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
  /contacts/{contactId}/send-direct-linkedin-inmail:
    post:
      tags: [Direct Outreach]
      operationId: sendDirectLinkedInInMail
      summary: Send a LinkedIn InMail to a contact
      description: >
        Sends a direct LinkedIn InMail outside of any sequence. Requires a
        Sales Navigator account with available InMail credits and the
        `contacts:operate` scope.
      parameters:
        - $ref: "#/components/parameters/contactId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [linkedInAccountId, subject, body]
              properties:
                linkedInAccountId:
                  type: integer
                  description: ID of the LinkedIn account to send from.
                subject:
                  type: string
                  maxLength: 200
                  description: InMail subject line.
                body:
                  type: string
                  maxLength: 1900
                  description: InMail body text.
      responses:
        "200":
          description: InMail dispatched.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendDirectLinkedInResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
  /contacts/{contactId}/send-direct-linkedin-message:
    post:
      tags: [Direct Outreach]
      operationId: sendDirectLinkedInMessage
      summary: Send a LinkedIn message to a contact
      description: >
        Sends a direct LinkedIn message outside of any sequence. The caller
        must already be connected with the contact on LinkedIn. Requires the
        `contacts:operate` scope.
      parameters:
        - $ref: "#/components/parameters/contactId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [linkedInAccountId, message]
              properties:
                linkedInAccountId:
                  type: integer
                  description: ID of the LinkedIn account to send from.
                message:
                  type: string
                  maxLength: 8000
                  description: Message body text.
      responses:
        "200":
          description: Message dispatched.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendDirectLinkedInResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
  /contacts/{contactId}/send-direct-linkedin-voice:
    post:
      tags: [Direct Outreach]
      operationId: sendDirectLinkedInVoice
      summary: Send a LinkedIn voice message to a contact
      description: >
        Sends a pre-recorded voice message via LinkedIn outside of any
        sequence. The caller must already be connected with the contact.
        Upload audio via `POST /v3/attachments/voice` to obtain a
        `voiceAttachmentId`. Requires the `contacts:operate` scope.
      parameters:
        - $ref: "#/components/parameters/contactId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [linkedInAccountId, voiceAttachmentId]
              properties:
                linkedInAccountId:
                  type: integer
                  description: ID of the LinkedIn account to send from.
                voiceAttachmentId:
                  type: integer
                  description: ID of the uploaded voice attachment.
      responses:
        "200":
          description: Voice message dispatched.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendDirectLinkedInResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
  /contacts/{contactId}/send-direct-linkedin-ai-voice:
    post:
      tags: [Direct Outreach]
      operationId: sendDirectLinkedInAiVoice
      summary: Send an AI-generated LinkedIn voice message to a contact
      description: >
        Generates and sends an AI voice message via LinkedIn outside of any
        sequence. The voice is synthesized from the provided script using the
        account's text-to-speech settings. The caller must already be
        connected with the contact. Requires the `contacts:operate` scope.
      parameters:
        - $ref: "#/components/parameters/contactId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [linkedInAccountId, script]
              properties:
                linkedInAccountId:
                  type: integer
                  description: ID of the LinkedIn account to send from.
                script:
                  type: string
                  maxLength: 1000
                  description: Text script to synthesize into an AI voice message.
      responses:
        "200":
          description: AI voice message dispatched.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SendDirectLinkedInResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >
        Reply.io API key sent as a Bearer token. Create one in the Reply.io app under
        Settings → API Key. See https://docs.reply.io/api-reference/authentication.
  parameters:
    contactId:
      name: contactId
      in: path
      required: true
      description: Contact ID.
      schema:
        type: integer
  schemas:
    SendDirectEmailResponse:
      type: object
      properties:
        status:
          type: string
          enum: [new, sent]
          description: Sending status.
        messageId:
          type: string
          description: Message ID of the sent email.
    SendDirectLinkedInResponse:
      type: object
      description: Response returned after a direct LinkedIn action is dispatched.
      properties:
        actionType:
          type: string
          enum: [connect, inMail, message, voice, aiVoice]
          description: The type of LinkedIn action performed.
        linkedInActivityId:
          type: integer
          description: ID of the LinkedIn activity record created for this send.
        sentAtUtc:
          type: string
          format: date-time
          description: When the action was dispatched (UTC).
    Problem:
      type: object
      description: RFC 7807 problem document (application/problem+json).
      properties:
        type: { type: string }
        title: { type: string }
        status: { type: integer }
        detail: { type: string }
        code:
          type: string
          description: >
            Stable machine-readable error slug on business errors, e.g.
            `contact.contactInBlackList` or `contact.emailAccountNotFound`.
  responses:
    BadRequest:
      description: >
        Validation failure or business rule rejection (e.g. email account
        missing, contact in blacklist, no LinkedIn profile URL).
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    Forbidden:
      description: The API key is missing a required scope.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
    RateLimited:
      description: >
        Rate limit exceeded (100 requests/minute, 3,000/hour per user). Honor the
        Retry-After header.
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
