Appearance
Planning
7 reads, no writes, on train 2026-11. Scopes: planning:read.
| Operation | Method | Scope | Path |
|---|---|---|---|
| One commitment's plan of record at cost | GET | planning:read | /api/v1/commitments/{commitment_id}/money |
| Every locked version of one commitment's plan of record | GET | planning:read | /api/v1/commitments/{commitment_id}/money/plan/versions |
| List the buy plans | GET | planning:read | /api/v1/mfp |
| The plan side of the Open to buy ledger for one plan's fiscal year | GET | planning:read | /api/v1/mfp/{mfp_id}/plan-side |
| The buy plan's grid | GET | planning:read | /api/v1/mfp/{mfp_id}/table |
| Every published version of a buy plan | GET | planning:read | /api/v1/mfp/{mfp_id}/versions |
| Get otb rollup | GET | planning:read | /api/v1/otb/{mfp_id}/rollup |
One commitment's plan of record at cost
GET /api/v1/commitments/{commitment_id}/money
Scopes: planning:read
One commitment's plan of record at cost, in integer cents, on the commitment's own 4-5-4 fiscal months (each keyed by the calendar year-month it maps to).
| Required | In | What it is |
|---|---|---|
commitment_id | path | The commitment's id. |
| Optional | In | What it is |
|---|---|---|
Tightly-Version | header | The date train to answer on. |
bash
curl "https://api.app.tightly.io/api/v1/commitments/<commitment_id>/money" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What it answers
json
{
"data": {
"categories": [
{
"category": "Outerwear",
"markdown_left_to_close_cents": 22400000,
"on_order_cents": [
48200000,
0
],
"open_to_commit_cents": [
3200000,
null
],
"planned_closing_stock_cents": [
98400000,
null
],
"planned_markdown_cents": [
14720000,
16880000
],
"planned_markdown_to_close_cents": 31600000,
"planned_sales_cents": [
184000000,
211000000
],
"realised_markdown": [
9200000,
null
],
"realised_markdown_at_retail": [
21400000,
null
],
"realised_markdown_to_date_cents": 9200000,
"realised_markdown_units": [
4120,
null
]
…
}
]
}
}What it refuses
- 401
key_invalid: "The API key is not valid." 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
plan_excludes: "This organisation's plan does not include Commitments. It is sold with Pro." The key may not reach this operation.plan_excludeswhen the plan of record is sold with Pro and this organisation's plan does not include it;scope_missingwhen the key does not hold Planning;organization_mismatchwhen the path names an organisation that is not the key's;ip_not_allowedwhen the caller's address is outside the key's allowlist. - 429
Every locked version of one commitment's plan of record
GET /api/v1/commitments/{commitment_id}/money/plan/versions
Scopes: planning:read
Every locked version of one commitment's plan of record, newest first, with the version that is the budget flagged. Metadata only: the label, who locked it, when, and how many cells it holds.
| Required | In | What it is |
|---|---|---|
commitment_id | path | The commitment's id. |
| Optional | In | What it is |
|---|---|---|
Tightly-Version | header | The date train to answer on. |
bash
curl "https://api.app.tightly.io/api/v1/commitments/<commitment_id>/money/plan/versions" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What it answers
json
{
"data": {
"absence": null,
"budget": {
"label": "Budget, pre-season",
"version_id": 18
},
"versions": [
{
"cell_count": 96,
"is_budget": false,
"kind": "reforecast",
"label": "Reforecast, September",
"locked_at": "2026-09-01T16:40:05Z",
"locked_by": "Dana Whitfield",
"version_id": 31
},
{
"cell_count": 96,
"is_budget": true,
"kind": "budget",
"label": "Budget, pre-season",
"locked_at": "2026-06-12T10:21:44Z",
"locked_by": "Dana Whitfield",
"version_id": 18
}
]
},
"message": {
"desc": "",
"service": "commitments",
"severity": "INFO"
}
}What it refuses
- 401
key_invalid: "The API key is not valid." 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
plan_excludes: "This organisation's plan does not include Commitments. It is sold with Pro." The key may not reach this operation.plan_excludeswhen the plan of record is sold with Pro and this organisation's plan does not include it;scope_missingwhen the key does not hold Planning;organization_mismatchwhen the path names an organisation that is not the key's;ip_not_allowedwhen the caller's address is outside the key's allowlist. - 429
List the buy plans
GET /api/v1/mfp
Scopes: planning:read
Every buy plan this organisation holds, newest first, with the fiscal year each covers and whether it has been published. One id from here addresses every other Planning operation.
| Optional | In | What it is |
|---|---|---|
Tightly-Version | header | The date train to answer on. |
bash
curl "https://api.app.tightly.io/api/v1/mfp" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What it answers
json
{
"data": [
{
"baseline_source": "last_year_actuals",
"cell_count": 96,
"created_at": "2026-08-03T09:14:22+00:00",
"fiscal_year": 2027,
"fiscal_year_end_month": 1,
"fiscal_year_start_month": 2,
"mfp_id": "mfp_7f21c9",
"name": "FY27 buy plan",
"status": "live",
"total_planned_revenue": 48200000
},
{
"baseline_source": "last_year_actuals",
"cell_count": 96,
"created_at": "2025-08-11T10:02:47+00:00",
"fiscal_year": 2026,
"fiscal_year_end_month": 1,
"fiscal_year_start_month": 2,
"mfp_id": "mfp_41b0da",
"name": "FY26 buy plan",
"status": "live",
"total_planned_revenue": 44900000
}
],
"message": {
"desc": "",
"service": "mfp",
"severity": "INFO"
}
}What it refuses
- 401
key_invalid: "The API key is not valid." 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
scope_missing: "This key cannot read Planning." The key may not reach this operation.plan_excludeswhen the planning rail is sold with Pro and this organisation's plan does not include it;scope_missingwhen the key does not hold Planning;organization_mismatchwhen the path names an organisation that is not the key's;ip_not_allowedwhen the caller's address is outside the key's allowlist. - 429
The plan side of the Open to buy ledger for one plan's fiscal year
GET /api/v1/mfp/{mfp_id}/plan-side
Scopes: planning:read
The plan side of the Open to buy ledger, the weekly sales and stock intake sheet, for one plan's fiscal year, at category by calendar month, and the same figure set at three levels: each cell, each category's year, and the whole year.
| Required | In | What it is |
|---|---|---|
mfp_id | path | The plan's id, from List the buy plans. |
| Optional | In | What it is |
|---|---|---|
include_plan_of_record | query | Read commitment plan-of-record enrichment. |
Tightly-Version | header | The date train to answer on. |
bash
curl "https://api.app.tightly.io/api/v1/mfp/<mfp_id>/plan-side" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What it answers
json
{
"data": {
"categories": [
{
"category": "Knitwear",
"cells": [
{
"figures": {
"budget": {
"basis": "unit_cost",
"cents": null,
"reason": "The buying budget is declared for the whole book, not per category.",
"usd": null
},
"chase_reserve": {
"basis": "unit_cost",
"cents": null,
"reason": "Planned depth carries no delivery date, so it belongs to no month.",
"usd": null
},
"cogs_plan": {
"basis": "unit_cost",
"cents": 69920000,
"reason": null,
"usd": 699200
},
"intake_committed": {
"basis": "unit_cost",
"cents": 51400000,
"reason": null,
"usd": 514000
},
"plan_receipts_cost": {
"basis": "unit_cost",
"cents": null,
"reason": "February has no planned closing stock, so the receipt identity did not fire.",
"usd": null
},
"sales_plan": {
"basis": "net_sales"
…
}
}
}
]
}
]
}
}What it refuses
- 401
key_invalid: "The API key is not valid." 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
scope_missing: "This key cannot read Planning." The key may not reach this operation.scope_missingwhen the key does not hold Planning;organization_mismatchwhen the path names an organisation that is not the key's;ip_not_allowedwhen the caller's address is outside the key's allowlist. - 404 Plan not found
- 429
The buy plan's grid
GET /api/v1/mfp/{mfp_id}/table
Scopes: planning:read
One buy plan's grid: the planned figure for every category and month of the fiscal year, with actuals filled in for months that have closed. metric=revenue reads the sales plan and metric=margin the margin plan.
| Required | In | What it is |
|---|---|---|
mfp_id | path | The plan to read. |
| Optional | In | What it is |
|---|---|---|
filter_args | query | URL-encoded JSON array of filters with keys category and sales_channel_id (operation eq/in), e.g. |
metric | query | Value to render in each cell. Defaults to revenue. |
view | query | Which version to read: "working" (default), "live", or a version_id. |
Tightly-Version | header | The date train to answer on. |
bash
curl "https://api.app.tightly.io/api/v1/mfp/<mfp_id>/table" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What it answers
json
{
"data": {
"filters": {
"categories": [
"Knitwear"
],
"sales_channel_ids": null
},
"hierarchy": {
"level": "department",
"reason": null,
"words": {
"category": "Class",
"department": "Division",
"subcategory": "Subclass"
}
},
"metric": "revenue",
"mfp": {
"baseline_source": "last_year_actuals",
"created_at": "2026-08-03T09:14:00+00:00",
"fiscal_year": 2027,
"fiscal_year_end_month": 1,
"fiscal_year_start_month": 2,
"mfp_id": "mfp_01J9X4",
"name": "FY27 Plan",
"status": "live"
},
"months": [
{
"is_completed": true,
"label": "Feb 27",
"month": 2,
"year": 2027
},
{
"is_completed": false,
"label": "Mar 27",
"month": 3,
"year": 2027
…
}
]
}
}What it refuses
- 401
key_invalid: "The API key is not valid." 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
plan_excludes: "This organisation's plan does not include Planning. It is sold with Pro." The key may not reach this operation.plan_excludeswhen the planning rail is sold with Pro and this organisation's plan does not include it;scope_missingwhen the key does not hold Planning;organization_mismatchwhen the path names an organisation that is not the key's;ip_not_allowedwhen the caller's address is outside the key's allowlist. - 429
Every published version of a buy plan
GET /api/v1/mfp/{mfp_id}/versions
Scopes: planning:read
Every published version of one buy plan, newest first, each with its totals and whether it is the version the plan currently reads as live, plus the working tip and whether it holds changes nobody has published.
| Required | In | What it is |
|---|---|---|
mfp_id | path | The plan's id, from List the buy plans. |
| Optional | In | What it is |
|---|---|---|
Tightly-Version | header | The date train to answer on. |
bash
curl "https://api.app.tightly.io/api/v1/mfp/<mfp_id>/versions" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What it answers
json
{
"data": {
"versions": [
{
"cogs_plan_usd": 18317800,
"is_live": true,
"label": "Published 1 Sep 2026",
"published_at": "2026-09-01T16:40:05Z",
"published_by": "Dana Whitfield",
"sales_plan_usd": 48210000,
"version_id": 14
},
{
"cogs_plan_usd": 17922400,
"is_live": false,
"label": "Published 4 Aug 2026",
"published_at": "2026-08-04T08:55:12Z",
"published_by": "Dana Whitfield",
"sales_plan_usd": 46980000,
"version_id": 11
}
],
"working": {
"cogs_plan_usd": 18317800,
"has_unpublished_changes": true,
"label": "Working",
"sales_plan_usd": 48210000
}
},
"message": {
"desc": "",
"service": "mfp",
"severity": "INFO"
}
}What it refuses
- 401
key_invalid: "The API key is not valid." 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
scope_missing: "This key cannot read Planning." The key may not reach this operation.plan_excludeswhen the planning rail is sold with Pro and this organisation's plan does not include it;scope_missingwhen the key does not hold Planning;organization_mismatchwhen the path names an organisation that is not the key's;ip_not_allowedwhen the caller's address is outside the key's allowlist. - 404 Plan not found
- 429
Get otb rollup
GET /api/v1/otb/{mfp_id}/rollup
Scopes: planning:read
Open to buy for one season, category by category: the envelope the plan declares, what is already committed, what is reserved against it, what is left, the forward recommended buy and the chase reserve split.
| Required | In | What it is |
|---|---|---|
mfp_id | path | The plan whose envelope this rollup is read against. |
| Optional | In | What it is |
|---|---|---|
scope | query | SS (Spring/Summer, the default) or FW (Fall/Winter), each narrows to a single season's half-open window. |
filter_args | query | URL-encoded JSON array of filters with keys category and sales_channel_id (operation eq/in), e.g. |
sort_args | query | Optional (TIG-2216). |
search | query | Optional (TIG-2216). |
Tightly-Version | header | The date train to answer on. |
bash
curl "https://api.app.tightly.io/api/v1/otb/<mfp_id>/rollup" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What it answers
json
{
"data": {
"basis": "cost",
"filters": {
"categories": null,
"sales_channel_ids": null
},
"guardrail": {
"absolute_ceiling_ratio": null,
"amber_threshold_ratio": 0.1,
"guardrail_tolerance_ratio": 0,
"mode": "soft_warning",
"surplus_threshold_ratio": 0.15
},
"mfp": {
"fiscal_year": 2027,
"mfp_id": "mfp_01J9X4",
"name": "FY27 Plan"
},
"notes": {
"category_scope": "mfp_selected_only",
"continuity_forward_estimate": "open_replen_demand_forecast_cost",
"cost_basis": "po_unit_cost_standard_not_landed"
},
"phase": "pre_season",
"rows": [
{
"active_mode": null,
"category": "Knitwear",
"chase_reserve": 91000,
"committed": 622400,
"committed_continuity": 210800,
"committed_seasonal": 411600,
"continuity_consumption_ratio": 0.32,
"continuity_envelope_alert": false,
"current_excluded_amount": 0,
"estimated": false,
"exclusion_active": false,
"forward_continuity_estimate": 61400,
"forward_recommended_buy": 168200
…
}
]
}
}What it refuses
- 400
validation_error: "scope[0]: Must be one of: SS, FW."scope=FYis refused: the rollup is a per-season pool, and rolling two disjoint selling windows into one running variance produced a crossing week that meant nothing. An unknown sort column is refused here too. - 401
key_invalid: "The API key is not valid." 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
plan_excludes: "This organisation's plan does not include Planning. It is sold with Pro." The key may not reach this operation.plan_excludeswhen the planning rail is sold with Pro and this organisation's plan does not include it;scope_missingwhen the key does not hold Planning;organization_mismatchwhen the path names an organisation that is not the key's;ip_not_allowedwhen the caller's address is outside the key's allowlist. - 429