Skip to main content

Subscription CRUD

Subscription API payloads use camelCase. Server-managed fields are returned in responses but rejected in write requests.

Operation semantics

OperationUse whenRiskConfirmation
GET /api/v1/subscriptionsSearch, summarize, analyze, or identify subscriptionsLowNot required
GET /api/v1/subscriptions/{id}Fetch one known record by UUIDLowNot required
POST /api/v1/subscriptionsRecord a new subscriptionMediumRequired for AI agents
PATCH /api/v1/subscriptions/{id}Change writable fields, including status (cancel/pause/resume)Medium–HighRequired for AI agents
DELETE /api/v1/subscriptions/{id}Permanently remove a tracked record (prefer cancel to keep history)HighRequired for AI agents
For AI integrations, list subscriptions before updating or deleting unless the user already supplied a subscription UUID. Write operations require an API key with the write scope; read-only keys receive 403 insufficient_scope.

Pagination

GET /api/v1/subscriptions returns results newest first and accepts two optional query parameters:
ParameterDefaultRule
limit50Page size, 1 to 100
offset0Number of records to skip from the start
The response includes a pagination object. Fetch the next page with offset + limit while hasMore is true.
{
  "data": [ /* ... */ ],
  "pagination": { "limit": 50, "offset": 0, "hasMore": false },
  "requestId": "request-..."
}
curl -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  "https://your-site.example/api/v1/subscriptions?limit=50&offset=50"
Out-of-range values return 400 invalid_pagination.

Filtering and sorting

GET /api/v1/subscriptions accepts optional filters and a sort order alongside pagination:
ParameterRule
statusOne of active, paused, cancelled
categoryExact category match, e.g. Streaming
periodOne of monthly, yearly, custom
qCase-insensitive search over the subscription name
expiringBeforeYYYY-MM-DD; returns records whose nextPaymentDate is on or before this date
sortOne of createdAt, -createdAt, nextPaymentDate, -nextPaymentDate, amount, -amount, name, -name (default -createdAt; - prefix is descending)
Combine status=active with expiringBefore to answer “what renews soon”. Invalid values return 400 invalid_query with a field and suggestedFix.
curl -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  "https://your-site.example/api/v1/subscriptions?status=active&expiringBefore=2026-07-01&sort=nextPaymentDate"
The response echoes the applied query so an agent can confirm what produced the result:
{
  "data": [ /* ... */ ],
  "pagination": { "limit": 50, "offset": 0, "hasMore": false },
  "query": { "sort": "nextPaymentDate", "filters": { "status": "active", "expiringBefore": "2026-07-01" } },
  "requestId": "request-..."
}

Subscription object

{
  "id": "33333333-3333-4333-8333-333333333333",
  "name": "Netflix",
  "category": "Streaming",
  "amount": 15.99,
  "currency": "USD",
  "period": "monthly",
  "lastPaymentDate": "2026-06-01",
  "nextPaymentDate": "2026-07-01",
  "notificationEnabled": true,
  "status": "active",
  "createdAt": "2026-06-16T00:00:00.000Z",
  "updatedAt": "2026-06-16T00:00:00.000Z"
}

Writable fields

{
  "name": "Netflix",
  "category": "Streaming",
  "amount": 15.99,
  "currency": "USD",
  "period": "monthly",
  "lastPaymentDate": "2026-06-01",
  "customDate": null,
  "notificationEnabled": true,
  "status": "active"
}
The server manages:
  • id
  • nextPaymentDate
  • createdAt
  • updatedAt
Do not send server-managed fields in POST or PATCH requests.

Field rules

FieldRule
nameRequired, 1-120 characters
categoryRequired, 1-80 characters
amountRequired number, 0 to 999999.99, max 2 decimals
currencyRequired. One of CNY, USD, EUR, JPY, GBP, AUD, CAD, CHF, HKD, SGD
periodRequired. One of monthly, yearly, custom
lastPaymentDateRequired date in YYYY-MM-DD format
customDateRequired only when period is custom; positive whole-number string of days
notificationEnabledOptional boolean, defaults to true
statusOptional. One of active, paused, cancelled; defaults to active
nextPaymentDate is calculated by the server from lastPaymentDate, period, and customDate. On PATCH, it is recalculated only when one of those three fields changes; updating any other field leaves the stored nextPaymentDate untouched. Only the fields you send in a PATCH are validated, so updating one field never fails because of unrelated stored data.

Lifecycle: cancel, pause, resume vs delete

A subscription has a status: active (billing), paused (temporarily stopped), or cancelled (no longer billing, kept for history). Changing status is a soft operation — the record and its audit history survive and can be reactivated. DELETE is permanent and removes the record and its audit history. Prefer a status change when the user says they stopped using a service but might come back:
# Cancel (keep history)
curl -X PATCH \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"cancelled"}' \
  https://your-site.example/api/v1/subscriptions/33333333-3333-4333-8333-333333333333

# Pause, then later resume
curl -X PATCH ... -d '{"status":"paused"}' .../{id}
curl -X PATCH ... -d '{"status":"active"}'  .../{id}
Only active subscriptions count toward active spend in the analytics endpoints. Use DELETE only when the user wants the record gone permanently.

Natural language examples

Add a subscription

User intent:
Add ChatGPT Plus, 20 USD per month, last paid on 2026-06-17.
API call:
curl -X POST \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"ChatGPT Plus","category":"AI","amount":20,"currency":"USD","period":"monthly","lastPaymentDate":"2026-06-17","notificationEnabled":true}' \
  https://your-site.example/api/v1/subscriptions

Change billing period

User intent:
Change Netflix to yearly billing.
Agent flow:
  1. Call GET /api/v1/subscriptions to identify the Netflix record.
  2. Ask the user to confirm the exact record and change.
  3. Call PATCH /api/v1/subscriptions/{id}.
curl -X PATCH \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"period":"yearly"}' \
  https://your-site.example/api/v1/subscriptions/33333333-3333-4333-8333-333333333333

Remove a duplicate

User intent:
Delete the duplicate Netflix subscription.
Agent flow:
  1. Call GET /api/v1/subscriptions and find duplicate candidates.
  2. Ask the user to confirm the exact subscription name and id.
  3. Call DELETE /api/v1/subscriptions/{id}.
curl -X DELETE \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  https://your-site.example/api/v1/subscriptions/33333333-3333-4333-8333-333333333333

Custom billing period

For non-monthly/non-yearly intervals, set period to custom and send customDate as a positive whole-number string of days.
{
  "name": "Domain renewal",
  "category": "Infrastructure",
  "amount": 12,
  "currency": "USD",
  "period": "custom",
  "lastPaymentDate": "2026-06-01",
  "customDate": "45"
}
When patching from custom back to monthly or yearly, omit customDate. The server clears stale custom billing data.

AI tool schema

Use ai-tools.json for tool/function definitions, risk levels, and confirmation prompts.