---
title: "Approve and, when eligible, synchronously materialize a proposal"
method: POST
path: "/accountant/companies/{customerId}/schedule-proposals/{id}/approve"
tags: ["Accountant Portal", "Schedule Proposals"]
---

# Approve and, when eligible, synchronously materialize a proposal

`POST /accountant/companies/{customerId}/schedule-proposals/{id}/approve`

## Request

- Base URL: `https://api.medianfi.com/api/v1`
- URL: `https://api.medianfi.com/api/v1/accountant/companies/{customerId}/schedule-proposals/{id}/approve`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Re-runs deterministic QA and synchronously materializes a human-approved proposal when detection and single-write are enabled. Human approval does not require the unattended auto-post flag. If single-write or detection is disabled, the row stays approved and the response reports approved_not_materialized. Re-approving resumes any partial write; posted rows are 200 no-ops. Other terminal rows return 409.

## Request body

- ApproveScheduleProposalInput
  - `editedInputs` object: Optional replacement derived inputs (validated per proposal type; 422 on schema violations).
  - `reviewNotes` string

## Response `200`

The approved proposal

- ScheduleProposalApprovalResponse
  - `success` boolean
  - `data` object: One AI-detected deferral/accrual candidate with its derived schedule inputs and confidence. Never a book write on its own; materialization (#1266 Phase B) turns approved proposals into schedule definitions.
    - `id` string, uuid
    - `companyId` string, uuid
    - `entityId` string, uuid, nullable
    - `currency` string, nullable
    - `proposalType` 'deferral' | 'accrual'
    - `kind` 'prepaid_amortization' | 'deferred_revenue_recognition' | 'accrual'
    - `derivedInputs` object: Zod-validated derived inputs (per-type schema; legs are ScheduleProposalLeg items).
    - `totalAmountCents` integer
    - `startDate` string, date
    - `endDate` string, date, nullable
    - `confidencePct` number
    - `confidenceBand` 'high' | 'medium' | 'low'
    - `confidenceComponents` object
    - `detectionSignals` object
    - `reasoning` string, nullable
    - `sourceKind` string
    - `sourceId` string, nullable
    - `dedupKey` string
    - `inputHash` string
    - `matchKey` string, nullable
    - `status` 'proposed' | 'pending_review' | 'approved' | 'rejected' | 'blocked' | 'posted' | 'superseded' | 'expired'
    - `blockedReason` string, nullable
    - `escalatedAt` string, date-time, nullable
    - `escalationReason` string, nullable
    - `metadata` object
      - `clarification` ScheduleProposalClarification
        - `state` 'draft' | 'sent', required
        - `question` string, required
        - `draftedAt` string, date-time, required
        - `sentAt` string, date-time
        - `sentByUserId` string, uuid, nullable
        - `threadId` string, uuid
        - `initialMessageId` string, uuid
        - `thread` ScheduleProposalClarificationThread
          - `id` string, uuid, required
          - `status` 'open' | 'resolved' | 'archived', required
          - `ownership` 'ai' | 'accountant', required
          - `blocksClose` false, required
          - `initialMessageId` string, uuid, required
          - `hasCustomerReply` boolean, required
          - `customerReplyCount` integer, required
          - `lastCustomerReplyAt` string, date-time, nullable
    - `reviewedBy` string, uuid, nullable
    - `reviewedAt` string, date-time, nullable
    - `reviewNotes` string, nullable
    - `editedInputs` object, nullable
    - `scheduleDefinitionIds` string[], nullable
    - `postedJeId` string, uuid, nullable
    - `createdBy` string, uuid, nullable
    - `createdAt` string, date-time
    - `updatedAt` string, date-time
    - `materialization` ScheduleProposalMaterialization
      - `status` 'posted' | 'already_posted' | 'approved_not_materialized' | 'blocked', required
      - `reason` string: Present when a gate or deterministic QA check prevents a write.
      - `scheduleDefinitionIds` string[]
      - `postedJeId` string, uuid, nullable

## Other responses

- `401`: Authentication required
- `403`: Insufficient permissions or not a company member
- `404`: Resource not found
- `409`: The proposal is in a state that cannot be approved
- `422`: Request failed validation

---

[API](https://skmtc.dev/medianfi/apis/median-api.md) · [All operations](https://skmtc.dev/medianfi/apis/median-api/llms.txt) · [OpenAPI document](https://skmtc.dev/medianfi/apis/median-api/revisions/20090ea32b0d?raw)
