# sponsorships.packages.save

Create or revise a reusable sponsorship package using existing invoice templates where selected. Store benefits and optional price without setting any tax rate or issuing an invoice. Revision checked, idempotent and audited.

`POST /api/v1/accounting/sponsorships.packages.save`

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

MCP tool: `accounting_sponsorships_packages_save`

## Fields

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `id` | string (ID) |  | The record's ID. |
| `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. |
| `name` | string | Yes | A display name. 1–200 characters. |
| `kind` | enum | Yes | Which kind of record or job this is. One of: `hackathon`, `retreat`, `event`, `initiative`, `other`. |
| `description` | string | Yes | A short description. at most 2000 characters. |
| `currency` | string | Yes | Three-letter currency code, such as CAD or USD. |
| `priceMinor` | string or null | Yes | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. |
| `benefits` | array of objects | Yes | at most 50 items. |
| `benefits[].id` | string (ID) | Yes | The record's ID. |
| `benefits[].description` | string | Yes | A short description. 1–500 characters. |
| `benefits[].performanceFrom` | string or null | Yes |  |
| `benefits[].performanceTo` | string or null | Yes |  |
| `benefits[].state` | enum | Yes | One of: `unknown`, `promised`, `delivered`, `cancelled`. |
| `benefits[].sources` | array of objects | Yes | at most 10 items. |
| `benefits[].sources[].kind` | enum | Yes | Which kind of record or job this is. One of: `receipt_original`, `mail_original`, `company_original`, `entry_note`. |
| `benefits[].sources[].id` | string (ID) | Yes | The record's ID. |
| `benefits[].sources[].sha256` | string | Yes | The SHA-256 hash of the file's exact bytes, as 64 lowercase hex characters. SHA-256 hash as 64 lowercase hex characters. |
| `benefits[].sources[].quote` | string | Yes | 1–1000 characters. |
| `benefits[].sources[].page` | integer or null |  | Default `null`. |
| `invoiceTemplate` | object or null |  | Default `null`. |
| `invoiceTemplate.title` | string |  | A short title. at most 200 characters. |
| `invoiceTemplate.currency` | string |  | Three-letter currency code, such as CAD or USD. |
| `invoiceTemplate.lines` | array of objects | Yes | 1–100 items. |
| `invoiceTemplate.lines[].id` | string |  | The record's ID. at most 64 characters. |
| `invoiceTemplate.lines[].description` | string | Yes | A short description. 1–500 characters. |
| `invoiceTemplate.lines[].quantity` | string |  |  |
| `invoiceTemplate.lines[].unitAmountMinor` | string |  | An amount in cents (the currency's smallest unit), written as a whole-number string such as "1250" for $12.50. Whole number written as a string, may be negative. |
| `invoiceTemplate.lines[].isHeader` | boolean |  |  |
| `invoiceTemplate.lines[].taxCode` | enum |  | One of: `standard`, `exempt`, `zero_rated`. |
| `invoiceTemplate.notes` | string |  | Notes kept with the record. at most 4000 characters. |
| `invoiceTemplate.memo` | string |  | at most 4000 characters. |
| `invoiceTemplate.paymentMethodIds` | array of strings (ID) |  | A list of record IDs. at most 10 items. |
| `invoiceTemplate.taxTreatment` | enum or null |  | One of: `auto`, `unknown`, `hst`, `gst`, `gst_qst`, `zero_rated`, `exempt`, `none`, `custom`. |
| `invoiceTemplate.taxRateCode` | enum or string or null |  |  |
| `invoiceTemplate.terms` | object |  | No other fields. |
| `invoiceTemplate.terms.code` | enum | Yes | One of: `due_on_receipt`, `net_7`, `net_15`, `net_30`, `net_45`, `net_60`, `custom`, `split_50_50`, `monthly`. |
| `invoiceTemplate.terms.days` | integer or null |  |  |
| `invoiceTemplate.payUrl` | string (uri) or null |  |  |
| `invoiceTemplate.projectId` | string (ID) or null |  |  |
| `archived` | boolean |  | Whether the record is archived. Default `false`. |
| `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/sponsorships.packages.save \
  -H "Authorization: Bearer $OATMILK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "expectedRevision": 3,
  "name": "Synthetic Ventures Inc.",
  "kind": "hackathon",
  "description": "Synthetic example from the docs",
  "currency": "CAD",
  "priceMinor": "1",
  "benefits": [
    {
      "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
      "description": "Synthetic example from the docs",
      "performanceFrom": "2026-09-30",
      "performanceTo": "2026-09-30",
      "state": "unknown",
      "sources": [
        {
          "kind": "receipt_original",
          "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
          "sha256": "9f2b5c1d7e3a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c",
          "quote": "USD"
        }
      ]
    }
  ]
}'
```

Reference page: https://app.getoatmilk.com/docs/api/sponsorships.packages.save
