# handoff.update

Record one handoff item with its owner, source, state, original documents and current revision. Filing acceptance requires retained confirmation. Stores no full payroll identity numbers and creates no postings.

`POST /api/v1/accounting/handoff.update`

Permissions: `accounting:read`, `accounting:write` · Roles: admin, finance · Idempotency key required · Send `expectedRevision`

Not available over MCP: Accountants' corrections grant isn't offered over MCP yet: phase 1 is read and comment only.

## Fields

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `from` | string (date) | Yes | The first date to include, as YYYY-MM-DD. |
| `to` | string (date) | Yes | The last date to include, as YYYY-MM-DD. |
| `key` | enum | Yes | One of: `engagement`, `prior_returns`, `prior_balances`, `workpapers`, `cra_income`, `cra_gst`, `cra_payroll`, `payroll_provider`, `t2_filing`, `gst_filing`, `payroll`, `contractor_identity`. |
| `item` | object | Yes | No other fields. |
| `item.status` | enum | Yes | Only include records with this status. One of: `not_started`, `requested`, `received`, `reviewed`, `prepared`, `submitted`, `accepted`, `not_applicable`. |
| `item.owner` | string | Yes | at most 200 characters. |
| `item.source` | string | Yes | Where the record came from. at most 500 characters. |
| `item.note` | string | Yes | A short note, kept with the record. at most 1000 characters. |
| `item.requestId` | string (ID) or null |  |  |
| `item.documentIds` | array of strings (ID) | Yes | A list of record IDs. at most 20 items. |
| `item.confirmation` | string | Yes | at most 200 characters. |
| `item.authorizationEndsOn` | string (date) or null | Yes |  |
| `item.calendarYear` | integer or null | Yes | A calendar year, such as 2026. |
| `item.payrollChecks` | array of enum values | Yes | One of: `employee_identity`, `full_calendar_year`, `gross_pay`, `taxable_benefits`, `cpp_cpp2`, `ei`, `income_tax`, `pension_adjustment`, `remittances`, `provider_reconciled`, `slips_delivered`, `filing_confirmation`. at most 12 items. |
| `item.payrollReviews` | array of objects |  | at most 6 items. Default `[]`. |
| `item.payrollReviews[].calendarYear` | integer | Yes | A calendar year, such as 2026. 1990 to 2100. |
| `item.payrollReviews[].checks` | array of enum values | Yes | One of: `employee_identity`, `full_calendar_year`, `gross_pay`, `taxable_benefits`, `cpp_cpp2`, `ei`, `income_tax`, `pension_adjustment`, `remittances`, `provider_reconciled`, `slips_delivered`, `filing_confirmation`. at most 12 items. |
| `item.payrollReviews[].documentIds` | array of strings (ID) | Yes | A list of record IDs. at most 20 items. |
| `item.payrollReviews[].register` | object |  | No other fields. |
| `item.payrollReviews[].register.payrollAccount` | string or null | Yes |  |
| `item.payrollReviews[].register.provider` | string | Yes | at most 200 characters. |
| `item.payrollReviews[].register.coversFullCalendarYear` | boolean | Yes |  |
| `item.payrollReviews[].register.providerEmployeeCount` | integer or null | Yes |  |
| `item.payrollReviews[].register.providerGrossPayMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.remittedMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.expectedRemittanceMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees` | array of objects | Yes | at most 100 items. |
| `item.payrollReviews[].register.employees[].employeeReference` | string | Yes | 1–100 characters. |
| `item.payrollReviews[].register.employees[].name` | string | Yes | A display name. 1–200 characters. |
| `item.payrollReviews[].register.employees[].provinceOfEmployment` | string | Yes | Two-letter country or province code. |
| `item.payrollReviews[].register.employees[].identityOriginalId` | string (ID) | Yes | The ID of the related record. |
| `item.payrollReviews[].register.employees[].grossPayMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].taxableBenefitsMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].cppMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].cpp2Minor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].eiMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].incomeTaxMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].pensionableEarningsMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].insurableEarningsMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].pensionAdjustmentMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.payrollReviews[].register.employees[].dentalCoverageCode` | integer or null | Yes |  |
| `item.engagementSetup` | object |  | No other fields. |
| `item.engagementSetup.firm` | string | Yes | at most 200 characters. |
| `item.engagementSetup.contact` | string | Yes | at most 200 characters. |
| `item.engagementSetup.approver` | string | Yes | at most 200 characters. |
| `item.engagementSetup.workFrom` | string (date) or null | Yes |  |
| `item.engagementSetup.workTo` | string (date) or null | Yes |  |
| `item.engagementSetup.responsibilities` | array of enum values | Yes | One of: `bookkeeping`, `month_end`, `year_end`, `gst_hst`, `payroll`, `t2_return`, `information_slips`. at most 7 items. |
| `item.engagementSetup.systemOfRecord` | enum or null | Yes | One of: `oatmilk`, `quickbooks`, `xero`, `sage`, `spreadsheet`, `other`. |
| `item.engagementSetup.systemNote` | string | Yes | at most 300 characters. |
| `item.engagementSetup.deliverables` | array of objects | Yes | at most 6 items. |
| `item.engagementSetup.deliverables[].kind` | enum | Yes | Which kind of record or job this is. One of: `financial_statements`, `t2_return`, `gst_hst_return`, `t4_slips`, `t4a_slips`, `bookkeeping_close`. |
| `item.engagementSetup.deliverables[].responsible` | enum | Yes | One of: `company`, `accountant`, `predecessor`, `payroll_provider`. |
| `item.engagementSetup.deliverables[].dueOn` | string (date) or null | Yes |  |
| `item.engagementSetup.deliverables[].state` | enum | Yes | One of: `not_started`, `prepared`, `reviewed`, `approved`, `not_applicable`. |
| `item.engagementSetup.deliverables[].approvedOn` | string (date) or null | Yes |  |
| `item.records` | array of objects |  | at most 4 items. |
| `item.records[].kind` | enum | Yes | Which kind of record or job this is. One of: `t2_schedules_gifi`, `assessments_correspondence`, `gst_hst_filings`, `payroll_slip_files`, `financial_statements`, `trial_balance_ledger`, `opening_balances`, `adjusting_entries`, `asset_cca_continuity`, `shareholder_loan_continuity`, `receivables_payables`, `unresolved_questions`. |
| `item.records[].state` | enum | Yes | One of: `available`, `missing`, `requested`, `received`, `reviewed`, `not_applicable`. |
| `item.records[].ownership` | enum | Yes | One of: `client`, `predecessor_working_paper`, `unknown`. |
| `item.records[].holder` | string | Yes | at most 200 characters. |
| `item.records[].owner` | string | Yes | at most 200 characters. |
| `item.records[].documentIds` | array of strings (ID) | Yes | A list of record IDs. at most 20 items. |
| `item.authorization` | object |  | No other fields. |
| `item.authorization.state` | enum | Yes | One of: `not_requested`, `requested`, `approved`, `revoked`. |
| `item.authorization.representative` | string | Yes | at most 200 characters. |
| `item.authorization.completedBy` | string | Yes | at most 200 characters. |
| `item.authorization.requestedOn` | string (date) or null | Yes |  |
| `item.authorization.approvedOn` | string (date) or null | Yes |  |
| `item.authorization.revokedOn` | string (date) or null | Yes |  |
| `item.filings` | array of objects |  | at most 8 items. |
| `item.filings[].periodFrom` | string (date) | Yes |  |
| `item.filings[].periodTo` | string (date) | Yes |  |
| `item.filings[].filedBy` | string | Yes | at most 200 characters. |
| `item.filings[].filedOn` | string (date) | Yes | A date, as YYYY-MM-DD. |
| `item.filings[].confirmation` | string | Yes | at most 200 characters. |
| `item.filings[].amended` | boolean | Yes |  |
| `item.filings[].amountPaidMinor` | integer or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `item.filings[].assessment` | enum | Yes | One of: `not_received`, `assessed`, `reassessed`. |
| `item.filings[].assessedOn` | string (date) or null | Yes |  |
| `item.filings[].documentIds` | array of strings (ID) | Yes | A list of record IDs. at most 20 items. |
| `item.balanceWorkpapers` | array of objects |  | at most 3 items. |
| `item.balanceWorkpapers[].kind` | enum | Yes | Which kind of record or job this is. One of: `opening_balances`, `trial_balance`, `closing_adjustments`. |
| `item.balanceWorkpapers[].asOf` | string (date) | Yes |  |
| `item.balanceWorkpapers[].approvedBy` | string | Yes | at most 200 characters. |
| `item.balanceWorkpapers[].source` | string | Yes | Where the record came from. at most 300 characters. |
| `item.balanceWorkpapers[].lines` | array of objects | Yes | 1–120 items. |
| `item.balanceWorkpapers[].lines[].account` | string | Yes | 1–120 characters. |
| `item.balanceWorkpapers[].lines[].gifi` | string or null | Yes |  |
| `item.balanceWorkpapers[].lines[].debitMinor` | integer | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. 0 to 100000000000. |
| `item.balanceWorkpapers[].lines[].creditMinor` | integer | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. 0 to 100000000000. |
| `item.balanceWorkpapers[].lines[].memo` | string | Yes | at most 200 characters. |
| `item.balanceWorkpapers[].documentIds` | array of strings (ID) | Yes | A list of record IDs. at most 20 items. |
| `expectedRevision` | integer | Yes | The record's current revision, from the last time you read it. If someone changed the record since, the request is refused with a conflict so you can reload and check before trying again. 0 to 9007199254740991. |
| `idempotencyKey` | string | Yes | Any unique text you generate once per intended change, so a retried request only happens once. Send it as the Idempotency-Key header instead if you prefer; if you send both they must match. 8–200 characters. |

## Example request

```bash
curl https://app.getoatmilk.com/api/v1/accounting/handoff.update \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "from": "2026-09-01",
  "to": "2026-09-30",
  "key": "engagement",
  "item": {
    "status": "not_started",
    "owner": "example",
    "source": "example",
    "note": "Synthetic example from the docs",
    "documentIds": [
      "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
    ],
    "confirmation": "example",
    "authorizationEndsOn": "2026-09-01",
    "calendarYear": 2026,
    "payrollChecks": [
      "employee_identity"
    ]
  },
  "expectedRevision": 3
}'
```

Reference page: https://app.getoatmilk.com/docs/api/handoff.update
