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

# GetDecisionAPI mirrors erdo_get_decision: one decision in full — the


> commitment, its exact actions and how each ended, its declared effects and the evidence that settled them, and supersession in both directions. A slug belonging to another organization reads as NotFound, so it cannot be probed for existence.




## OpenAPI

````yaml /api/openapi.json get /v1/decisions/{decisionSlug}
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, and experiments. Authenticate with
    a Bearer API key (erdo_api_...) or scoped token (erdo_token_...).
  version: '2026-08-07'
servers:
  - url: https://api.erdo.ai
    description: Production
security:
  - bearerAuth: []
paths:
  /v1/decisions/{decisionSlug}:
    get:
      summary: |
        GetDecisionAPI mirrors erdo_get_decision: one decision in full — the
      description: >
        commitment, its exact actions and how each ended, its declared effects
        and the evidence that settled them, and supersession in both directions.
        A slug belonging to another organization reads as NotFound, so it cannot
        be probed for existence.
      operationId: GET:mcp.GetDecisionAPI
      parameters:
        - allowEmptyValue: true
          explode: false
          in: path
          name: decisionSlug
          required: true
          schema:
            type: string
          style: simple
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  actions:
                    items:
                      $ref: '#/components/schemas/types.DecisionActionDetail'
                    type: array
                  decision:
                    $ref: '#/components/schemas/types.DecisionSummary'
                  effects:
                    items:
                      $ref: '#/components/schemas/types.DecisionEffectDetail'
                    type: array
                  lineage:
                    $ref: '#/components/schemas/types.DecisionLineage'
                type: object
          description: Success response
        default:
          $ref: '#/components/responses/APIError'
components:
  schemas:
    types.DecisionActionDetail:
      description: |
        what became of it.
      properties:
        action_key:
          description: >
            NOT here: it belongs to the execution path, and copying a provider
            payload into a read API would make this a second, unbounded store of
            provider data with its own leakage surface.
          title: >
            ActionKey is the registered action that ran (a tool key). The exact
            input is
          type: string
        authorized_by_approval_id:
          description: |
            through — the indexed reverse lineage from the approval side.
          format: uuid
          title: >
            AuthorizedByApprovalID is the approval whose decision let this exact
            call
          type: string
        completed_at:
          format: date-time
          type: string
        effective_at:
          description: >
            is what an effect's exposure boundaries are anchored on — never
            approval time, which can precede the change by days.
          format: date-time
          title: >
            EffectiveAt is the instant reality changed, set only on confirmed
            success. It
          type: string
        execution_status:
          description: >
            failed, or unknown. \`unknown\` is egress that may or may not have
            landed — a crash between the provider call and the write — and it is
            neither a failure nor a success; it is resolved by provider
            readback, never by a retry.
          title: >
            ExecutionStatus is this action's single attempt: pending, started,
            succeeded,
          type: string
        result_summary:
          description: |
            never the raw response.
          title: >
            ResultSummary is the provider-owned bounded projection of what came
            back,
          type: string
        started_at:
          format: date-time
          type: string
        subject_kind:
          description: >
            the envelope's subject — a batch pausing four ad groups has one
            campaign-level intent and four distinct subjects.
          title: >
            SubjectKind / SubjectLabel name what THIS action touched, which is
            not always
          type: string
        subject_label:
          type: string
      title: >
        DecisionActionDetail is one exact external call the decision authorized,
        and
      type: object
    types.DecisionSummary:
      description: >
        continuing agent and a returning operator read.


        It is the same shape for both audiences on purpose. The agent's copy of
        what the organization has committed to cannot be the only one, or a
        human coming back to the workstream has no way to check what it is
        acting on — so the projection carries decision, rationale, authority,
        execution and supersession, and both surfaces render those five facts
        rather than each deriving their own.


        It is deliberately not the whole record: the envelope's declared
        effects, its per-action inputs and its measurement contract are a
        drill-in, and putting them here would push the ledger read past what a
        model can usefully hold.
      properties:
        applicability:
          description: >
            that outlives the work it authorized, or an authorization for
            exactly the actions it named. It travels with the summary because
            the two are read differently — a standing decision is continued
            until something supersedes it, while a one\_shot in the current list
            is work still in flight to see through, not a strategy to keep
            running.
          title: >
            Applicability is standing or one_shot: whether this decision is a
            course
          type: string
        approval_id:
          description: >
            approval producer family. It travels on the row so the join runs in
            both directions from one read: a caller holding an approval filters
            the list by it, and a caller reading a page of decisions can index
            them by the approvals they answer without a call per row.
          format: uuid
          title: >
            ApprovalID is the ask this decision was made on, present only for
            the
          type: string
        authorized_at:
          format: date-time
          type: string
        decided_by_user_id:
          format: uuid
          type: string
        decider_kind:
          description: |
            deterministic\_policy, safe\_default or system.
          title: |
            DeciderKind is who exercised the judgement: human, llm_agent,
          type: string
        decider_rationale:
          description: >
            they supplied any. Usually empty — approving a card records no
            sentence.
          title: >
            DeciderRationale is the deciding human's own words about the answer,
            when
          type: string
        decision_class:
          description: >
            (\`paid\_media.ad\_group.pause\`), provider-agnostic and stable
            across the tools that implement it. Source is the producer family
            that recorded it.
          title: |
            DecisionClass is what KIND of decision this is
          type: string
        evidence_kind:
          description: >
            specified state exists), experimental (a controlled comparison), or
            observational (a before/after movement with no control). Empty when
            the settled effects disagree — a decision with one experimental and
            one deterministic result has no single kind, and picking one would
            attach the stronger claim to both.
          title: |
            EvidenceKind is what KIND of evidence settled it: deterministic (the
          type: string
        execution_status:
          description: >
            change the decision authorized actually reached the outside world.
            pending | started | succeeded | failed | unknown, and empty when the
            decision authorized no action at all.
          title: >
            ExecutionStatus rolls the child actions up into one word: whether
            the
          type: string
        outcome_label:
          description: >
            says the same thing about the same row rather than each deriving its
            own vocabulary from the pair above. Only an experiment may say a
            change "worked"; deterministic evidence says "confirmed" and
            observational says "moved as expected".


            Empty whenever the pair cannot support one phrase — mixed outcomes,
            mixed evidence kinds — because there is no honest single sentence
            for those and the per-effect labels in the detail read are the
            answer.
          title: >
            OutcomeLabel is the server's own words for this outcome, so every
            surface
          type: string
        outcome_status:
          description: >
            not\_met, inconclusive, or \`mixed\` when they disagree. Empty while
            nothing has settled, which is a different fact from inconclusive —
            one says the answer is not in yet, the other says the evidence could
            not produce one.


            Effects that are unmeasurable, censored, or parked in
            measurement\_unavailable contribute nothing here. They are coverage,
            they are visible as such in the detail read and the scorecard, and
            folding them in would let a decision nobody could measure read as
            one that did not work.
          title: >
            OutcomeStatus rolls this decision's SETTLED effects into one word:
            met,
          type: string
        proposed_at:
          format: date-time
          type: string
        slug:
          type: string
        source:
          type: string
        status:
          type: string
        subject_kind:
          description: |
            names it — a campaign, a page, a lead form.
          title: >
            SubjectKind / SubjectLabel name what the decision is about, as the
            operator
          type: string
        subject_label:
          type: string
        superseded_by_decision_slug:
          description: >
            decision that later replaced this one. It carries the same
            past-authorization rule the currentness read applies — a reversal
            somebody PROPOSED and a human then refused has not replaced
            anything, and showing it here would report a course as retired
            exactly when the human voted to keep it.
          title: >
            SupersededByDecisionSlug is the other direction of the same link:
            the
          type: string
        supersedes_decision_slug:
          description: >
            the producer declared the link explicitly — a reversal states what
            it reverses, and nothing infers it from two decisions sharing a
            subject.
          title: >
            SupersedesDecisionSlug names the decision this one replaced. Set
            only when
          type: string
        what:
          description: >
            PRODUCER's rationale — the argument the proposal was made on. When a
            human gave their own reason for the answer it is in
            DeciderRationale, never merged into this one: reading a proposal's
            argument as the approver's reason for accepting it is how a record
            starts attributing motives.
          title: |
            What is the decision in one line of business English; Why is the
          type: string
        why:
          type: string
      title: |
        DecisionSummary is the compact projection of one decision that both a
      type: object
    types.DecisionEffectDetail:
      description: |
        evidence that answered it.
      properties:
        baseline_value:
          description: |
            qualify them.
          title: >
            The measured values behind the outcome, with the sample and
            freshness that
          type: number
        baseline_window_days:
          format: int32
          maximum: 2147483647
          minimum: -2147483648
          type: integer
        binding_kind:
          type: string
        confidence:
          type: number
        conflict_decision_id:
          description: >
            effect — present only on a censored row, and never inferred from two
            changes sharing a subject.
          format: uuid
          title: >
            ConflictDecisionID is the producer-declared overlap that censored
            this
          type: string
        contract_version:
          type: string
        dataset_metric:
          title: >
            DatasetMetric names the synced-dataset metric a dataset-bound effect
            reads.
          type: string
        evaluator_id:
          description: >
            outcome can be re-derived or invalidated when that evaluator turns
            out to be wrong.
          title: >
            EvaluatorID / EvaluatorVersion are who settled it and with what
            code, so an
          type: string
        evaluator_version:
          type: string
        evidence_freshness_at:
          format: date-time
          type: string
        evidence_kind:
          type: string
        first_exposure_at:
          description: >
            multi-action change: the earliest confirmed prerequisite ends the
            baseline so a partial rollout cannot contaminate it, and the latest
            opens the outcome window so the comparison only covers the
            intervention fully in force.
          format: date-time
          title: >
            FirstExposureAt / FullyEffectiveAt are the two exposure boundaries
            of a
          type: string
        fully_effective_at:
          format: date-time
          type: string
        guardrails:
          description: >
            "spend fell but leads collapsed" a not\_met rather than a win on the
            primary metric alone.
          items:
            $ref: '#/components/schemas/types.DecisionEffectGuardrail'
          title: >
            Guardrails are secondary predicates that must ALSO hold, which is
            what makes
          type: array
        measurability:
          description: >
            a declaration that claimed to be measurable and got the shape wrong.
            The third is reported rather than discarded, because a scorecard
            that silently drops malformed contracts reports better coverage the
            worse its producers get.
          title: >
            Measurability is measurable, unmeasurable, or
            unmeasurable_invalid_contract —
          type: string
        measured_at:
          format: date-time
          type: string
        metric:
          description: >
            Measurement is always a read of data Erdo already holds, never a
            live call to a provider.
          title: >
            Metric, and the typed binding that says where the number is read
            from.
          type: string
        min_delta:
          type: number
        outcome:
          description: |
            the pair. Empty until the effect settles.
          title: >
            Outcome, the evidence kind that justifies it, and the server's own
            words for
          type: string
        outcome_label:
          type: string
        outcome_value:
          type: number
        outcome_window_days:
          format: int32
          maximum: 2147483647
          minimum: -2147483648
          type: integer
        sample_size:
          format: int32
          maximum: 2147483647
          minimum: -2147483648
          type: integer
        settled_at:
          format: date-time
          type: string
        source_observation_count:
          description: >
            The ids themselves stay in the database: they are an audit handle,
            not something a reader of this API can resolve, and dumping a few
            hundred UUIDs into every detail read would bury the numbers that
            matter.
          format: int64
          title: >
            SourceObservationCount is how many immutable input rows the outcome
            rests on.
          type: integer
        status:
          description: >
            cannot be read yet), measuring, grace (waiting on fresh data),
            settled or censored.
          title: >
            Status is where this effect is: pending, measurement_unavailable
            (its binding
          type: string
        success_operator:
          description: |
            increase\_by and decrease\_by read MinDelta.
          title: |
            The success predicate: at_most and at_least read TargetValue,
          type: string
        target_value:
          type: number
        unmeasurable_reason:
          description: >
            a malformed contract, or — on an inconclusive outcome — whether the
            data never arrived, the sample was too thin, or a guardrail could
            not be confirmed.
          title: >
            UnmeasurableReason says why there is no scoreable number: nothing to
            measure,
          type: string
        variant_key:
          type: string
      title: >
        DecisionEffectDetail is one declared expectation and, once it settles,
        the
      type: object
    types.DecisionLineage:
      description: |
        and what ended it.
      properties:
        superseded_by_slugs:
          description: >
            rather than one slug because nothing prevents two producers from
            each declaring they replaced the same course, and reporting only the
            first would hide the second reversal entirely.
          items:
            type: string
          title: >
            SupersededBySlugs are the later decisions that replaced this one. A
            list
          type: array
        supersedes_slug:
          title: |
            SupersedesSlug is the decision this one explicitly replaced.
          type: string
      title: >
        DecisionLineage is supersession in both directions — what this decision
        ended,
      type: object
    types.DecisionEffectGuardrail:
      description: |
        primary one.
      properties:
        metric:
          type: string
        min_delta:
          type: number
        operator:
          type: string
        target_value:
          type: number
      title: >
        DecisionEffectGuardrail is one secondary predicate, in the same shape as
        the
      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_...).

````