---
title: "Attach a negotiated price and/or record a tenant's monthly credit allocation"
method: PUT
path: "/v1/admin/tenants/{tenant_id}/subscription-package"
tags: ["admin"]
---

# Attach a negotiated price and/or record a tenant's monthly credit allocation

`PUT /v1/admin/tenants/{tenant_id}/subscription-package`

## Request

- Base URL: `https://api.patricia.app` (the document declares no server; this is the origin it was published from)
- URL: `https://api.patricia.app/v1/admin/tenants/{tenant_id}/subscription-package`
- Auth: HTTP bearer

Platform-admin only. With no negotiated_stripe_price_id this only edits the live allocation, as before. With one, it's validated against Stripe and attached — the live allocation is untouched until the customer subscribes and the webhook syncs it.

## Path parameters

- `tenant_id` string, uuid, required

## Request body

- AdminSubscriptionPackageUpdate: PUT body: the new allocation, an optional Stripe price to attach, and why.
  - `monthly_credit_grant` integer, required
  - `negotiated_stripe_price_id` string, nullable: A hand-made Stripe price to attach; validated against Stripe before it's saved.
  - `reason` string, required: Why this allocation is changing.
  - `expected_monthly_credit_grant` integer, nullable, required: The monthly_credit_grant GET most recently reported (null if it reported none, meaning the workspace had no row yet). A 409 means someone else changed it first: reload and re-apply.

## Response `200`

Successful Response

- AdminSubscriptionPackageView: The staff readout for a tenant's negotiated package.
  - `tenant_id` string, uuid, required
  - `monthly_credit_grant` integer, nullable, required: This tenant's own LIVE allocation, or null when the workspace has no subscription row yet.
  - `default_monthly_credit_grant` integer, required: The fleet default an unset workspace runs on.
  - `stripe_customer_id` string, nullable: This workspace's cus_… id, or null.
  - `stripe_subscription_id` string, nullable: The synced sub_… id, or null.
  - `cancelable_stripe_subscription_id` string, nullable: The sub_… a revert would cancel, or null when nothing bills the attached price.
  - `tier` string, nullable: The tier Stripe last synced onto this row.
  - `status` string, nullable: The subscription status Stripe last synced.
  - `current_period_end` string, date-time, nullable: The synced current period end.
  - `last_grant_at` string, date-time, nullable: When the monthly sweep last granted this row.
  - `negotiated_stripe_price_id` string, nullable: The attached negotiated price, or null.
  - `negotiated_monthly_price_cents` integer, nullable: Stripe's amount for that price.
  - `negotiated_monthly_credit_grant` integer, nullable: The negotiated allocation; promoted into monthly_credit_grant once synced.
  - `has_attached_package` boolean, required: Whether a negotiated price is attached — an offer, not a sale.
  - `on_negotiated_package` boolean, required: Whether the workspace is actually billing on it right now.

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden. Inspect response body `code` for the specific reason (for example: PERMISSION_DENIED, EMAIL_NOT_VERIFIED, TENANT_ACCESS_REQUIRED, ROLE_UNRESOLVED)
- `404`: Not found
- `409`: Conflict
- `422`: Validation error
- `503`: Billing is not configured on this deployment.

---

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