> ## 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.

# ResumeLeadNextActionsAPI hands a lead the desk snoozed (held) or stopped


> (closed) back to the follow-up engine.

The hold or close ends now: its revisit\_at becomes now and resumed\_at, resumed\_by and resumed\_by\_external\_ref record who ended it, so the engine decides for the lead again on its next pass (within fifteen minutes). It answers FailedPrecondition, "This lead is not snoozed or stopped.", when the lead's current decision is not a hold whose time is still running or a close.

There is deliberately no MCP tool for this, for the same reason as the hold, the close, the undo and the handoff answer: it is the desk's call about a lead it is dealing with, not a model's. A scoped API key is refused.




## OpenAPI

````yaml /api/openapi.json post /v1/datasets/{datasetSlug}/leads/{leadRef}/next-actions/resume
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-10-05'
servers:
  - url: https://api.erdo.ai
    description: Production
security:
  - bearerAuth: []
paths:
  /v1/datasets/{datasetSlug}/leads/{leadRef}/next-actions/resume:
    post:
      summary: |
        ResumeLeadNextActionsAPI hands a lead the desk snoozed (held) or stopped
      description: >
        (closed) back to the follow-up engine.


        The hold or close ends now: its revisit\_at becomes now and resumed\_at,
        resumed\_by and resumed\_by\_external\_ref record who ended it, so the
        engine decides for the lead again on its next pass (within fifteen
        minutes). It answers FailedPrecondition, "This lead is not snoozed or
        stopped.", when the lead's current decision is not a hold whose time is
        still running or a close.


        There is deliberately no MCP tool for this, for the same reason as the
        hold, the close, the undo and the handoff answer: it is the desk's call
        about a lead it is dealing with, not a model's. A scoped API key is
        refused.
      operationId: POST:mcp.ResumeLeadNextActionsAPI
      parameters:
        - allowEmptyValue: true
          explode: false
          in: path
          name: datasetSlug
          required: true
          schema:
            type: string
          style: simple
        - allowEmptyValue: true
          explode: false
          in: path
          name: leadRef
          required: true
          schema:
            type: string
          style: simple
      requestBody:
        content:
          application/json:
            schema:
              properties:
                decided_by_external_ref:
                  title: >
                    DecidedByExternalRef is YOUR OWN handle for the person
                    resuming the lead.

                    It is stored on the hold or close as
                    resumed_by_external_ref, beside the

                    one who held or closed it. Stored verbatim, never
                    interpreted. ATTRIBUTION,

                    NEVER AUTHENTICATION — see decided_by_external_ref on the
                    record endpoint.

                    At most 200 characters.
                  type: string
              type: object
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  accompanying_email:
                    $ref: '#/components/schemas/leadaction.AccompanyingEmailState'
                  action_input:
                    type: object
                  action_kind:
                    type: string
                  approval_request_id:
                    type: string
                  call_outcome:
                    $ref: '#/components/schemas/leadaction.CallOutcome'
                  canonical_lead_id:
                    type: string
                  created_by:
                    type: string
                  dataset_id:
                    type: string
                  decided_at:
                    format: date-time
                    type: string
                  decided_by:
                    type: string
                  decided_by_external_ref:
                    description: >
                      handles for the person who decided this action and the
                      person who took that decision back — opaque strings Erdo
                      stores verbatim and never interprets.


                      They exist because an application built on \`/v1\`
                      authenticates a whole team with ONE organization API key,
                      so DecidedBy — the Erdo account that key belongs to — is
                      the same person for every decision anybody there makes.
                      The caller is the only party that can say which of its
                      users acted, so it says, and it resolves these back to
                      names on its own side.


                      ATTRIBUTION, NEVER AUTHENTICATION. Anything holding the
                      organization's key can write any value here; Erdo does not
                      verify it, must never branch on it, authorize by it, or
                      resolve it against an Erdo identity. It is fine for
                      "Approved by Ana" on a card and must never be presented as
                      proof of who acted. Empty means the caller offered no
                      name, which is every decision made before this existed.
                    title: >
                      DecidedByExternalRef and UndoneByExternalRef are the
                      CALLING PRODUCT's own
                    type: string
                  due_at:
                    format: date-time
                    type: string
                  error:
                    type: string
                  evaluated_at:
                    format: date-time
                    type: string
                  executed_at:
                    format: date-time
                    type: string
                  execution_ref:
                    type: string
                  facts:
                    $ref: '#/components/schemas/leadaction.LeadFacts'
                  handoff_alert:
                    $ref: '#/components/schemas/leadaction.HandoffAlertState'
                  id:
                    type: string
                  lead_reference:
                    type: string
                  mode:
                    type: string
                  model:
                    type: string
                  playbook_revision:
                    format: int64
                    type: integer
                  policy_digest:
                    description: >
                      is what says whether the decision is stale — not the
                      revision number. The revision bumps on every save, so a
                      decision at revision 9 may have been decided under exactly
                      the rules revision 10 carries; comparing it with
                      LeadPlaybook.PolicyDigest answers the question the
                      revision only hints at. Empty on every decision written
                      before digests existed and on one a person recorded by
                      hand. See policy.go for the two halves it is made of.
                    title: >
                      PolicyDigest identifies the playbook this decision was
                      made under, and it
                    type: string
                  priority:
                    type: string
                  priority_reason:
                    type: string
                  rationale:
                    type: string
                  resumed_at:
                    description: >
                      handed the lead back to the engine (POST
                      .../next-actions/resume). The revisit time is set to the
                      same moment, so the engine decides for the lead on its
                      next pass. Absent on every decision that was never resumed
                      — a hold whose time simply ran out has none.
                    format: date-time
                    title: >
                      ResumedAt is when somebody at the desk ended this hold or
                      close early and
                    type: string
                  resumed_by:
                    title: |
                      ResumedBy is the Erdo account that resumed it.
                    type: string
                  resumed_by_external_ref:
                    description: >
                      who resumed it. Opaque, unverified, stored verbatim —
                      attribution, never authority, exactly as
                      DecidedByExternalRef.
                    title: >
                      ResumedByExternalRef is the calling product's own handle
                      for the person
                    type: string
                  revisit_at:
                    format: date-time
                    type: string
                  rule:
                    type: string
                  source:
                    type: string
                  stage:
                    type: string
                  status:
                    type: string
                  undone_by_external_ref:
                    type: string
                type: object
          description: Success response
        default:
          $ref: '#/components/responses/APIError'
components:
  schemas:
    leadaction.AccompanyingEmailState:
      description: >
        out beside a gated decision: whether it went, when, in what words, and
        why it did not if it did not.


        It joins the row's own columns (the authoritative state) with the draft
        stored in action\_input, so a caller does not have to read two things
        and work out which one is true.
      properties:
        body_markdown:
          type: string
        error:
          title: |
            Error is why it has not gone, in the words a person reads.
          type: string
        execution_ref:
          title: |
            ExecutionRef is the mailer's id for the email that went.
          type: string
        sent_at:
          format: date-time
          type: string
        status:
          title: >
            Status is pending, sending, sent, failed or skipped. See the
            constants.
          type: string
        subject:
          type: string
        to:
          type: string
      title: >
        AccompanyingEmailState is what a surface needs to render the email that
        went
      type: object
    leadaction.CallOutcome:
      properties:
        duration_seconds:
          format: int64
          type: integer
        status:
          description: >
            answered, "no-answer", "busy", "failed" and so on otherwise, and a
            ringing or in-progress status while the call has not ended.
          title: |
            Status is the call record's status: "completed" for a call somebody
          type: string
      title: >
        CallOutcome is how one placed call ended, in the call record's own
        words.
      type: object
    leadaction.LeadFacts:
      description: >
        read nothing.


        A decision now carries only Language, the language the lead wrote in,
        which is the engine's own reading. What the lead stated is read from the
        lead's details instead. The other fields are filled only on decisions
        made before the engine read details.
      properties:
        bedrooms:
          $ref: '#/components/schemas/leadaction.LeadFact'
        broker:
          $ref: '#/components/schemas/leadaction.LeadFact'
        budget:
          $ref: '#/components/schemas/leadaction.LeadFact'
        financing:
          $ref: '#/components/schemas/leadaction.LeadFact'
        language:
          $ref: '#/components/schemas/leadaction.LeadFact'
        purpose:
          $ref: '#/components/schemas/leadaction.LeadFact'
        timeline:
          $ref: '#/components/schemas/leadaction.LeadFact'
      title: >
        LeadFacts is what an evaluation read about the lead. A nil field means
        it
      type: object
    leadaction.HandoffAlertState:
      description: >
        desk a handoff is waiting: whether it went, when, the message to open,
        and why it did not go if it did not.


        The alert is the only MESSAGE a handoff produces. Carrying a handoff out
        is a person marking it done, which records the literal string "accepted"
        and sends nothing — so a surface that wants to show what a handoff SENT
        has to read this and not the row's execution ref, and one that wants to
        know whether the desk has dealt with the lead reads the row's status. It
        is also why these facts are exposed at all: a sent alert is a real,
        openable message, and the timeline used to show a handoff as an
        unopenable line that mentioned no email whatever.


        It reads only the row's own columns, unlike AccompanyingEmailState,
        because the alert's words are not drafted onto the decision — they are
        composed at send time from the card by buildHandoffAlert. The message
        itself is read back from the email service by ExecutionRef, which is the
        only copy of it there is.
      properties:
        error:
          description: |
            a person reads.
          title: >
            Error is why it has not gone, or which recipients it missed, in the
            words
          type: string
        execution_ref:
          description: >
            it. One alert is one message per recipient and this is the first
            copy that sent; every copy carries the same words, so it opens what
            the desk read. Empty on an alert that has not gone, and on one sent
            before the id was recorded (migration 013) — in which case there is
            nothing to open and a surface must not pretend otherwise.
          title: >
            ExecutionRef is the email service's id for the alert, which is what
            opens
          type: string
        sent_at:
          format: date-time
          type: string
        status:
          description: |
            constants in handoff\_alert.go.
          title: |
            Status is pending, sending, sent or failed. See the HandoffAlert*
          type: string
      title: >
        HandoffAlertState is what a surface needs to render the alert that told
        the
      type: object
    leadaction.LeadFact:
      description: |
        read it from, so a person can check the reading against the row.
      properties:
        source_column:
          type: string
        value:
          type: string
      title: >
        LeadFact is one thing the evaluation read about the lead and the column
        it
      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_...).

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.