REST API · billing

POST /billing/addons

Owner-only; requires a token minted with the billing scope. Subscribe to a monthly add-on that renews until cancelled — or until the subscription ends, after which the already-paid cycle stays active to its own end date and stops without further charges. Requires an active Pro/Scale subscription and a saved payment method.

Body: `{addonKey, project?}` — `project` (a slug the owner owns) is required for per-project add-ons (`rpm_*`). Returns `{addonKey, project, monthlyEurCents, transactionId, chargeStatus, activatedAt, nextRenewalAt}`. `transactionId`: The Paddle transaction id, or null while Paddle has not yet listed the charge. The purchase is recorded either way; do not retry it. `chargeStatus`: `pending` or `completed`.

post_api_billing_addons_subscribe

Authentication

  • Bearer

Request body

application/json · required

{
    "addonKey": "rpm_2000",
    "project": "support-desk"
}

Responses

201

The new add-on activation. `transactionId`: The Paddle transaction id, or null while Paddle has not yet listed the charge. The purchase is recorded either way; do not retry it. `chargeStatus`: `pending` or `completed`.

application/json

{
    "addonKey": "rpm_2000",
    "project": "support-desk",
    "quantity": 1,
    "monthlyEurCents": 2000,
    "totalEurCents": 2000,
    "transactionId": "txn_01hxq2p5f3z9v8t6r4m2n0k7j1",
    "chargeStatus": "completed",
    "activatedAt": "2026-01-15T09:00:00+00:00",
    "nextRenewalAt": "2026-02-14T09:00:00+00:00"
}

400

Unknown add-on, no active Pro/Scale subscription, or already active.

application/json

{
    "required": [
        "error"
    ],
    "properties": {
        "error": {
            "description": "Machine-readable error code, e.g. \"not_found\", \"forbidden\", \"validation\".",
            "type": "string",
            "enum": [
                "bad_request",
                "unauthorized",
                "insufficient_credits",
                "forbidden",
                "web_only",
                "allowlist_widens",
                "not_found",
                "conflict",
                "draft_revision_conflict",
                "rollback_confirmation_conflict",
                "not_replayable",
                "targets_outside_allowlist",
                "gone",
                "deprecated_operation",
                "validation",
                "rate_limited",
                "payment_provider",
                "verification_unavailable",
                "analytics_unavailable",
                "credential_vault_unavailable",
                "notification_grants_unavailable"
            ]
        },
        "message": {
            "description": "Human-readable explanation of what went wrong. Absent on a field-level validation failure, where `fields` carries the detail instead.",
            "type": "string"
        },
        "object": {
            "description": "The resource the error concerns, when there is one.",
            "required": [
                "type",
                "id"
            ],
            "properties": {
                "type": {
                    "type": "string"
                },
                "id": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "action": {
            "description": "What to do next, naming the call or the panel page that does it. Absent when there is no corrective step.",
            "type": "string"
        },
        "fields": {
            "description": "Present when `error` is \"validation\" — one message per rejected field, keyed by the field name.",
            "type": "object",
            "additionalProperties": {
                "type": "string"
            }
        },
        "retry_after": {
            "description": "An RFC 3339 timestamp, the moment the request may be retried. Not every `rate_limited` refusal carries one.",
            "type": "string",
            "format": "date-time"
        }
    },
    "type": "object"
}

409

A purchase of this add-on is still being confirmed with the payment provider; try again in a few minutes.

application/json

{
    "required": [
        "error"
    ],
    "properties": {
        "error": {
            "description": "Machine-readable error code, e.g. \"not_found\", \"forbidden\", \"validation\".",
            "type": "string",
            "enum": [
                "bad_request",
                "unauthorized",
                "insufficient_credits",
                "forbidden",
                "web_only",
                "allowlist_widens",
                "not_found",
                "conflict",
                "draft_revision_conflict",
                "rollback_confirmation_conflict",
                "not_replayable",
                "targets_outside_allowlist",
                "gone",
                "deprecated_operation",
                "validation",
                "rate_limited",
                "payment_provider",
                "verification_unavailable",
                "analytics_unavailable",
                "credential_vault_unavailable",
                "notification_grants_unavailable"
            ]
        },
        "message": {
            "description": "Human-readable explanation of what went wrong. Absent on a field-level validation failure, where `fields` carries the detail instead.",
            "type": "string"
        },
        "object": {
            "description": "The resource the error concerns, when there is one.",
            "required": [
                "type",
                "id"
            ],
            "properties": {
                "type": {
                    "type": "string"
                },
                "id": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "action": {
            "description": "What to do next, naming the call or the panel page that does it. Absent when there is no corrective step.",
            "type": "string"
        },
        "fields": {
            "description": "Present when `error` is \"validation\" — one message per rejected field, keyed by the field name.",
            "type": "object",
            "additionalProperties": {
                "type": "string"
            }
        },
        "retry_after": {
            "description": "An RFC 3339 timestamp, the moment the request may be retried. Not every `rate_limited` refusal carries one.",
            "type": "string",
            "format": "date-time"
        }
    },
    "type": "object"
}

← REST API reference