Public API (v1)
Base URL: https://krunc.com/api/v1 · Spec: /api/v1/openapi.json
The API is tenant-scoped: a key only ever sees the workspace it was created in. Create keys under Dashboard → API keys (admin role). The full key (cos_…) is shown once; we store a SHA-256 hash and a prefix.
Authentication
Authorization: Bearer cos_xxxxxxxxxxxxxxxx
Every endpoint requires a scope. A key without the scope gets 403 {"error":"forbidden","message":"This key lacks the [scope] scope."}.
| Scope | Grants |
|---|---|
read:profile | GET /profiles |
read:store | GET /products |
write:store | POST /products |
read:orders | GET /orders, GET /orders/{id} |
read:servers | GET /servers, GET /servers/{id}/status |
read:members | GET /members (display name + role only; users who hide their username appear as "Anonymous member") |
write:posts | POST /posts (creates a draft) |
read:analytics | GET /analytics/summary |
webhooks | GET/POST /webhooks, DELETE /webhooks/{id}, POST /webhooks/{id}/enable |
Rate limit: 300 requests / minute per key (X-RateLimit-Remaining header, 429 with Retry-After when exceeded).
Conventions
- Lists are paginated:
?page=1&per_page=25(max 100) →{"data":[…],"meta":{"page":1,"per_page":25,"total":7,"pages":1}} - Errors:
{"error":"not_found","message":"…"}with the matching HTTP status. Validation errors:422 {"error":"validation_failed","errors":{"field":["msg"]}} - Money is in integer cents with a 3-letter
currency. - Timestamps are UTC
YYYY-MM-DD HH:MM:SS.
Examples
curl -H "Authorization: Bearer $KEY" https://krunc.com/api/v1/me
curl -H "Authorization: Bearer $KEY" "https://krunc.com/api/v1/products?per_page=50"
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"name":"VIP","kind":"recurring","interval":"month","price_cents":999,"deliverables":[{"type":"discord_role","guild_id":"123","role_id":"456"}]}' \
https://krunc.com/api/v1/products
curl -H "Authorization: Bearer $KEY" https://krunc.com/api/v1/servers/48/status
GET /servers/{id}/status only returns public information: counts, status and zone aggregates that meet the server's minimum-count threshold. Individual player positions are never exposed through the API.
Outbound webhooks
Register a URL and the events you want:
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"url":"https://example.com/hooks/creatoros","events":["order.paid","entitlement.granted"]}' \
https://krunc.com/api/v1/webhooks
The response includes secret (whsec_…) once. Events: order.paid, order.refunded, subscription.created, subscription.canceled, entitlement.granted, entitlement.revoked, member.joined, stream.started, stream.ended, server.online, server.offline, post.published, automation.run, report.created, or *.
Each delivery is an HTTP POST with JSON body {id, event, created_at, tenant_id, data} and headers:
| Header | Meaning |
|---|---|
X-CreatorOS-Event | event name |
X-CreatorOS-Delivery | unique delivery id (use for idempotency) |
X-CreatorOS-Signature | sha256= + HMAC-SHA256 of the raw body with your secret |
Verify before trusting (PHP):
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_CREATOROS_SIGNATURE'] ?? '')) { http_response_code(400); exit; }
Respond with any 2xx within 10 seconds. Non-2xx responses are retried with exponential backoff (up to 5 attempts per delivery). After 10 consecutive failures the webhook is disabled and the workspace owners are notified; re-enable it with POST /webhooks/{id}/enable.
Internally any module can emit: $app->make('outbound')->emit($tenantId, 'order.paid', [...]).