Plans and usage
How SolidHooks bills per organisation, what counts as a delivery, what each plan includes, and how to read and change your plan over the API.
Billing is per organisation. A project never carries billing state. The billable unit is the delivery: one event routed to one connection.
For prices, see solidhooks.com/#pricing. Prices are not listed here because they live in the plan catalogue and can change.
What counts as a delivery
| Case | Billed? |
|---|---|
| One event routed to one connection | One delivery |
| One event fanned out to N connections | N deliveries |
| Automatic retries of a delivery | No. Retries reuse the same delivery. |
| A replay of an event | Yes. It creates new deliveries. |
| A duplicate (idempotent) request | No. It is never counted or blocked. |
| A request that fails verification (rejected event) | No. It creates no delivery. |
Manually retrying one delivery with POST /v1/deliveries/{id}/retry is free too, since no new delivery is created. See Retries.
Plans
Four tiers exist: Hobby (free, the default), Launch and Scale (self-serve), and Enterprise (sales-led). These are the seeded defaults for each entitlement. null means unlimited or custom.
| Entitlement | Hobby | Launch | Scale | Enterprise |
|---|---|---|---|---|
| Included deliveries per month | 10,000 | 50,000 | 1,000,000 | null |
| Event retention | 3 days | 7 days | 30 days | 90 days |
| Seats | 1 | 5 | null | null |
| Delivery attempts | 3 | 8 | 8 | 8 |
| Retry window | 1 hour | 36 hours | 36 hours | 36 hours |
| Deliveries per second | 10 | 50 | 200 | null |
| Delivery burst | 20 | 100 | 400 | null |
| Connectors | 0 | 1 | 5 | null |
| Replicas per connector | off | 2 | 5 | null |
| Overage | hard cap | metered | metered | negotiated |
Read live values from the API
The table shows seeded defaults. The database is the source of truth, and individual organisations can have overrides. Call GET /v1/billing/plan for the entitlements that apply to your organisation, and GET /v1/billing/plans for the catalogue.
A few of these are worth knowing:
- Attempts and retry window bound the retry schedule. Whichever runs out first ends it. Both are also clamped by retention.
- Delivery pacing never produces an error. A delivery over the rate waits and sends automatically; see Rate limits.
- Connectors gate private delivery. A plan with none cannot create one.
Read your plan and the catalogue
curl -s "$API/v1/billing/plan" \
-H "authorization: Bearer $SDHK_KEY"The response includes your active plan, its currency and priceCents, and the effective entitlements.
curl -s "$API/v1/billing/plans" \
-H "authorization: Bearer $SDHK_KEY"The catalogue lists plans in display order. Each plan carries currency and priceCents in your organisation's billing currency, plus a prices map of every supported currency for comparison. priceCents: null means custom (Enterprise). Amounts are minor units.
Currency
Prices are offered in GBP, USD and EUR. The owner's country at sign-up sets the organisation's currency, and it is fixed once the organisation first subscribes. GET /v1/countries lists the countries available for sign-up with their billing currency; it needs no authentication.
Usage
curl -s "$API/v1/billing/usage" \
-H "authorization: Bearer $SDHK_KEY"You get deliveries used against included, plus resetAt. On plans with overage it also returns accrued pay-as-you-go units and an estimated charge (currency, pricePer100kCents, estimatedCents) in your billing currency.
The usage window is the subscription's billing period for subscribed organisations, resetting at renewal. For everyone else it is the UTC calendar month.
Hard cap or overage
On Hobby, once the included deliveries are used, ingest rejects further requests until the window resets or you upgrade:
{
"code": "QUOTA_EXCEEDED",
"message": "Monthly delivery quota exceeded (10000/10000). ...",
"data": { "limit": 10000, "current": 10000, "resetAt": "2026-11-01T00:00:00.000Z" }
}The response is HTTP 429. Replays are gated by the same check, because they create billable deliveries.
Paid plans with overage enabled never hard-cap. Deliveries beyond the included quota are metered and billed as overage, and the plan's overage rate is in the catalogue.
SolidHooks emails organisation owners and admins when usage reaches 80% of the included quota and again at 100%. Each alert is sent once per usage window.
Change your plan
The first subscription is a browser step. Stripe Checkout runs in the dashboard at https://dash.solidhooks.com/settings/plan, and only an organisation owner or admin can start it. If you are an agent, ask your human to do it, then confirm with GET /v1/billing/plan.
After that, everything is API-driven:
| Endpoint | What it does |
|---|---|
POST /v1/billing/preview-plan-change | Prices an immediate change to { planKey } without making it. |
POST /v1/billing/change-plan | Changes between paid tiers. Upgrades apply immediately and invoice the prorated difference. Send scheduleAtPeriodEnd: true for a downgrade at period end. |
POST /v1/billing/revert-scheduled-change | Cancels a pending scheduled change. |
POST /v1/billing/cancel | Cancels at the end of the current period. |
POST /v1/billing/resume | Undoes a scheduled cancellation. |
POST /v1/billing/refresh | Drops the cached plan and returns the freshly resolved one. |
curl -s "$API/v1/billing/change-plan" \
-H "authorization: Bearer $SDHK_KEY" \
-H 'content-type: application/json' \
-d '{ "planKey": "scale" }'preview-plan-change returns available: false with a 200 when Stripe cannot price the change, and while a scheduled change is pending. Check available before showing a figure, and call revert-scheduled-change first if you need a quote. Hobby is a cancellation, not a plan change, and Enterprise is sales-led.
Permissions
The mutations (change-plan, preview-plan-change, revert-scheduled-change, cancel, resume) need a key scoped billing: read & write, or an owner or admin session. Anything less gets INSUFFICIENT_API_KEY_PERMISSIONS. The reads and refresh work with any key. See API keys.
Next steps
- Rate limits — ingest and delivery pacing
- Retries — how attempts and the retry window apply
- Errors —
QUOTA_EXCEEDEDand the other codes