---
title: "Confirm the four things Tightly must know about an account's reports, once per account"
method: POST
path: "/api/v1/sell-out/declaration"
tags: ["sell_out"]
---

# Confirm the four things Tightly must know about an account's reports, once per account

`POST /api/v1/sell-out/declaration`

## Request

- Base URL: `https://api.app.tightly.io`
- URL: `https://api.app.tightly.io/api/v1/sell-out/declaration`
- Auth: HTTP bearer

Records a person's answers for one account: `location_grain` (`store`, `dc` or `none`), `currency`, `price_tax_basis` and `absence_convention`, all four required in one call, with `account_id` in the query. Re-confirming REPLACES them, which is how a retailer that has changed its reporting week is corrected.

All four or none. A declaration missing one of them would have to be completed by a default, and the reason these four exist is that every default is wrong for about half of all accounts.

Values are checked here and again by the database, and the database constraints are the ones that hold; this validation exists so a person gets a sentence rather than a constraint violation.

Call it once per account before import_sell_out, and read it back with get_sell_out_declaration.

Sold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.

Scope: `sell_out:write`, which includes `sell_out:read`.

## Query parameters

- `account_id` string, required

## Headers

- `Tightly-Version` string
- `Idempotency-Key` string

## Request body

- object
  - `absence_convention` 'explicit_zero' | 'row_omitted', required: Whether a product missing from the file sold none (`explicit_zero`, they write a zero, so an absent row is genuinely absent data) or was simply not reported (`row_omitted`). This is the answer that cannot be defaulted.
  - `currency` string, required: Three-letter code, e.g. GBP or EUR.
  - `location_grain` 'store' | 'dc' | 'none', required: What a row covers, one store, one distribution centre, or the whole account.
  - `price_tax_basis` 'inclusive' | 'exclusive' | 'unknown', required: Whether the prices in the file include tax.
  - `source_profile_key` string: The format profile these answers were read from, when a file proposed them.
  - `week_anchor_dow` integer: The day their week ends on, 0 for Monday through 6 for Sunday. Absent when the export carries no date at all and there is no anchor to declare, inventing one would be the same defect the field exists to prevent.

## Response `200`

The stored declaration, in the same shape `GET /sell-out/declaration` serves, including `confirmed_by` and the `confirmed_by_name` resolved for it. This account's reports can now be imported.

- object

## Other responses

- `400`: No account was named, or one of the four is missing or outside its closed list. The refusal says which and what to answer.
- `401`: No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.
- `403`: The key may not reach this operation. `plan_excludes` when Sell-out is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Sell-out; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `account_id`, and the sentence names both accounts.
- `429`: The key has spent its allowance for this minute. 600 reads and 120 writes a minute, counted per key, refused calls included. `Retry-After` says how long to wait.
- `503`: The database is briefly out of reach, which is what a failover or a full connection pool looks like from outside. Wait the seconds `Retry-After` names and send the request again. A read is safe to repeat. A write is not, because the connection was lost in the middle of it, so read the object back before sending it a second time. Branch on the status rather than on `code`, which is `POSTGRES_UNAVAILABLE` from one of the two seams that raise this and `SERVICE_UNAVAILABLE` from the other.

---

[API](https://skmtc.dev/tightly/apis/tightly-api.md) · [All operations](https://skmtc.dev/tightly/apis/tightly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tightly/apis/tightly-api/revisions/199f7eb35afb?raw)
