Skip to content

Planning ​

7 reads, no writes, on train 2026-11. Scopes: planning:read.

OperationMethodScopePath
One commitment's plan of record at costGETplanning:read/api/v1/commitments/{commitment_id}/money
Every locked version of one commitment's plan of recordGETplanning:read/api/v1/commitments/{commitment_id}/money/plan/versions
List the buy plansGETplanning:read/api/v1/mfp
The plan side of the Open to buy ledger for one plan's fiscal yearGETplanning:read/api/v1/mfp/{mfp_id}/plan-side
The buy plan's gridGETplanning:read/api/v1/mfp/{mfp_id}/table
Every published version of a buy planGETplanning:read/api/v1/mfp/{mfp_id}/versions
Get otb rollupGETplanning: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).

RequiredInWhat it is
commitment_idpathThe commitment's id.
OptionalInWhat it is
Tightly-VersionheaderThe 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_excludes when the plan of record is sold with Pro and this organisation's plan does not include it; scope_missing when the key does not hold Planning; 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.
  • 429

Try it in the reference

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.

RequiredInWhat it is
commitment_idpathThe commitment's id.
OptionalInWhat it is
Tightly-VersionheaderThe 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_excludes when the plan of record is sold with Pro and this organisation's plan does not include it; scope_missing when the key does not hold Planning; 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.
  • 429

Try it in the reference

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.

OptionalInWhat it is
Tightly-VersionheaderThe 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_excludes when the planning rail is sold with Pro and this organisation's plan does not include it; scope_missing when the key does not hold Planning; 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.
  • 429

Try it in the reference

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.

RequiredInWhat it is
mfp_idpathThe plan's id, from List the buy plans.
OptionalInWhat it is
include_plan_of_recordqueryRead commitment plan-of-record enrichment.
Tightly-VersionheaderThe 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_missing when the key does not hold Planning; 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.
  • 404 Plan not found
  • 429

Try it in the reference

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.

RequiredInWhat it is
mfp_idpathThe plan to read.
OptionalInWhat it is
filter_argsqueryURL-encoded JSON array of filters with keys category and sales_channel_id (operation eq/in), e.g.
metricqueryValue to render in each cell. Defaults to revenue.
viewqueryWhich version to read: "working" (default), "live", or a version_id.
Tightly-VersionheaderThe 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_excludes when the planning rail is sold with Pro and this organisation's plan does not include it; scope_missing when the key does not hold Planning; 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.
  • 429

Try it in the reference

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.

RequiredInWhat it is
mfp_idpathThe plan's id, from List the buy plans.
OptionalInWhat it is
Tightly-VersionheaderThe 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_excludes when the planning rail is sold with Pro and this organisation's plan does not include it; scope_missing when the key does not hold Planning; 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.
  • 404 Plan not found
  • 429

Try it in the reference

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.

RequiredInWhat it is
mfp_idpathThe plan whose envelope this rollup is read against.
OptionalInWhat it is
scopequerySS (Spring/Summer, the default) or FW (Fall/Winter), each narrows to a single season's half-open window.
filter_argsqueryURL-encoded JSON array of filters with keys category and sales_channel_id (operation eq/in), e.g.
sort_argsqueryOptional (TIG-2216).
searchqueryOptional (TIG-2216).
Tightly-VersionheaderThe 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=FY is 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_excludes when the planning rail is sold with Pro and this organisation's plan does not include it; scope_missing when the key does not hold Planning; 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.
  • 429

Try it in the reference

Tightly API, version 2026-11.