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

# ReadLeadPlaybookAPI mirrors the erdo_read_lead_playbook MCP tool: the


> read-back of a draft, without saving it.




## OpenAPI

````yaml /api/openapi.json post /v1/lead-playbook/read-back
openapi: 3.0.0
info:
  title: Erdo API
  description: >-
    Erdo's REST API: query and write datasets, run agents, manage threads,
    integrations, pages, evals, workstreams, experiments, and bounded outreach.
    Authenticate with a Bearer API key (erdo_api_...) or scoped token
    (erdo_token_...).
  version: '2026-09-22'
servers:
  - url: https://api.erdo.ai
    description: Production
security:
  - bearerAuth: []
paths:
  /v1/lead-playbook/read-back:
    post:
      summary: |
        ReadLeadPlaybookAPI mirrors the erdo_read_lead_playbook MCP tool: the
      description: |
        read-back of a draft, without saving it.
      operationId: POST:mcp.ReadLeadPlaybookAPI
      requestBody:
        content:
          application/json:
            schema:
              properties:
                agent_id:
                  title: >
                    AgentID reads the draft back as saving it with that
                    concierge would. Omitted,

                    null or empty reads it back with none, which is what saving
                    with none does.
                  type: string
                body:
                  title: |
                    Body is the draft playbook text. Required. Nothing is saved.
                  type: string
              type: object
              required:
                - body
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  accompanying_email:
                    $ref: '#/components/schemas/leadaction.ReadBackAccompaniment'
                  actions:
                    items:
                      $ref: '#/components/schemas/leadaction.ReadBackAction'
                    type: array
                  cadence:
                    $ref: '#/components/schemas/leadaction.ReadBackCadence'
                  limits:
                    items:
                      $ref: '#/components/schemas/leadaction.ReadBackLimit'
                    type: array
                  priority:
                    $ref: '#/components/schemas/leadaction.ReadBackPriority'
                  stages:
                    items:
                      $ref: '#/components/schemas/leadaction.ReadBackStage'
                    type: array
                  working_hours:
                    $ref: '#/components/schemas/leadaction.WorkingHours'
                type: object
          description: Success response
        default:
          $ref: '#/components/responses/APIError'
components:
  schemas:
    leadaction.ReadBackAccompaniment:
      description: >
        goes out beside a gated text or call.


        Mode is "automatic" (an email may go on its own beside the card) or
        "never". There is deliberately no "approval": an accompanying email that
        itself needed approving would be a second thing in front of a person
        about one lead, which is exactly the shape this engine does not have.
      properties:
        mode:
          type: string
        when:
          description: |
            empty when the text does not say.
          title: >
            When is one short sentence from the text saying when it wants one,
            or
          type: string
      title: >
        ReadBackAccompaniment is what the playbook's text says about the email
        that
      type: object
    leadaction.ReadBackAction:
      description: |
        person. Mode is after platform limits.
      properties:
        kind:
          type: string
        mode:
          type: string
        when:
          type: string
      title: >
        ReadBackAction is one action kind and whether the text lets it run
        without a
      type: object
    leadaction.ReadBackCadence:
      description: >
        the per-channel gaps in each reachability state, and how soon after
        capture the engine may start. It is read out of the text like everything
        else in the read-back, and then clamped to what the platform allows.
      properties:
        confirmed:
          allOf:
            - $ref: '#/components/schemas/leadaction.ContactGaps'
          title: |
            Confirmed applies from the first inbound of any kind onwards.
        new_lead_grace_minutes:
          format: int64
          title: |
            NewLeadGraceMinutes is how long after capture a lead is left alone.
          type: integer
        unconfirmed:
          allOf:
            - $ref: '#/components/schemas/leadaction.ContactGaps'
          title: >
            Unconfirmed applies while the lead has never answered on any
            channel.
      title: >
        ReadBackCadence is how fast the playbook's text says Erdo may work a
        lead:
      type: object
    leadaction.ReadBackLimit:
      description: |
        renderer can say which rules the operator wrote and which Erdo applied.
      properties:
        kind:
          type: string
        mode:
          type: string
        reason:
          type: string
      title: >
        ReadBackLimit is a restriction the platform imposed on top of the text,
        so a
      type: object
    leadaction.ReadBackPriority:
      description: >
        the playbook's own rules put a lead at each level, and the factors the
        playbook forbids taking into account.


        The three levels are fixed — high, medium, low. They are the platform's,
        not the playbook's: the evaluation's response schema constrains the
        model to exactly those three (see evaluationFormat in evaluate.go) and
        the queue's sort, its filters and its badges are built on them. A
        playbook cannot invent a tier; what it decides is which of its rules map
        onto each level.


        NOTHING HERE IS ENFORCED, and this struct is not a guard. The model
        still chooses a decision's priority and writes its priority\_reason, and
        no decision is rejected, downgraded or rewritten because its priority
        disagrees with this ladder. The read-back exists so an operator can SEE
        what Erdo understood — until now the ladder reached the model only as
        raw playbook text, so a misread rule was invisible until decisions came
        out wrong. Whether to enforce it is a product decision nobody has taken.
      properties:
        forbidden_factors:
          description: >
            a lead's country, city, name or language, say. Empty when it forbids
            none.
          items:
            type: string
          title: >
            ForbiddenFactors are the things the text says must NOT affect
            priority —
          type: array
        high:
          description: >
            one short phrase each, in the text's own terms ("budget $2M or
            more", "they replied to us", "timeline beyond a year").
          items:
            type: string
          title: >
            High, Medium and Low are the conditions the text gives for each
            level,
          type: array
        low:
          items:
            type: string
          type: array
        medium:
          items:
            type: string
          type: array
      title: >
        ReadBackPriority is the priority ladder Erdo read out of a playbook:
        which of
      type: object
    leadaction.ReadBackStage:
      description: |
        from that stage.
      properties:
        actions:
          items:
            type: string
          type: array
        name:
          type: string
        summary:
          type: string
      title: >
        ReadBackStage is one stage the text describes, with the actions it
        allows
      type: object
    leadaction.WorkingHours:
      description: |
        lowercase three-letter names; Start and End are "HH:MM" in Timezone.
      properties:
        days:
          items:
            type: string
          type: array
        end:
          type: string
        start:
          type: string
        timezone:
          type: string
      title: |
        WorkingHours is the window automatic contact is allowed in. Days are
      type: object
    leadaction.ContactGaps:
      description: >
        channel. Zero means "the text did not say", which reads as the default,
        never as "no gap at all".


        BetweenChannels is how long after a contact on one channel the next
        contact on a DIFFERENT channel waits ("text them two minutes after the
        call"). Empty when the playbook sets no such wait.
      properties:
        between_channels:
          items:
            $ref: '#/components/schemas/leadaction.ChannelStep'
          type: array
        call_minutes:
          format: int64
          type: integer
        email_minutes:
          format: int64
          type: integer
        sms_minutes:
          format: int64
          type: integer
      title: |
        ContactGaps is the least time between two contacts to one lead on ONE
      type: object
    leadaction.ChannelStep:
      description: |
        contact on After, a contact on Next waits Minutes.
      properties:
        after:
          type: string
        minutes:
          format: int64
          type: integer
        next:
          type: string
      title: |
        ChannelStep is one wait the playbook sets between two channels: after a
      type: object
  responses:
    APIError:
      content:
        application/json:
          schema:
            externalDocs:
              url: https://pkg.go.dev/encore.dev/beta/errs#Error
            properties:
              code:
                description: Error code
                example: not_found
                externalDocs:
                  url: https://pkg.go.dev/encore.dev/beta/errs#ErrCode
                type: string
              details:
                description: Error details
                type: object
              message:
                description: Error message
                type: string
            title: APIError
            type: object
      description: Error response
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An Erdo API key (erdo_api_...) or scoped token (erdo_token_...).

````