# cStar — Full Developer Reference > Complete reference for the cStar API, SDKs, webhooks, and CLI. > Companion files: https://cstar.help/llms.txt (short index) — https://cstar.help/openapi.yaml (OpenAPI 3.1). > Drop-in CLAUDE.md / .cursor/rules: https://cstar.help/developers/cstar-ai-context.md > Source-of-truth: when this file disagrees with the installed dist or .d.ts, the dist wins. --- ## About cStar cStar is a customer support platform for small teams. Customers see clean professional support; agents see XP, boss battles, achievements, quests. Flat $15/seat. Built on SvelteKit + Supabase. --- ## The Code of cStar These are the opinions baked into the SDK. The rest of this file enforces them. ### 01. Bare resources, not envelopes. _You shouldn't have to type ".data" every single time._ Single-object methods return the object. `cstar.tickets.create(...)` resolves to the ticket. `cstar.tickets.get(id)` resolves to the ticket. Same for update. Metadata for the last call lives on `cstar.lastMeta` — `requestId`, `timestamp`, and `pagination` for list calls. Reach for it when you need it. Skip it when you don't. List endpoints are the one exception. They return `{ data, pagination, hasMore }` because pagination is part of the answer, not metadata about it. ```javascript // Single-object — bare return const ticket = await cstar.tickets.create({ title: 'Need help' }); console.log(ticket.id); // Need the request ID for support? It's right there. console.log(cstar.lastMeta.requestId); // Lists — paginated envelope const { data, pagination, hasMore } = await cstar.tickets.list({ status: 'open' }); console.log(`Page ${pagination.page} of ~${Math.ceil(pagination.total / pagination.pageSize)}`); ``` --- ### 02. Webhook events are the spine. _Polling is a tax. State changes should push, not pull._ Every state change in cStar fires a webhook. Tickets, customers, articles, messages, members, automations, billing — if something flipped, an event went out. Build integrations off the event surface. A new ticket arrives → your CRM gets a row. A customer turns into a paid plan → your finance Slack channel pings. The data is already in motion. Hook into it. Need to test locally without exposing a tunnel? `cstar listen --forward-to http://localhost:3000/webhooks`. Need to fire one without waiting for a real event? `cstar trigger ticket.created`. Subscribe in dev. No ngrok required. ```bash # Forward production events to your local server cstar listen --forward-to http://localhost:3000/webhooks # Fire a fake event to test the handler cstar trigger ticket.created ``` --- ### 03. Test mode is real mode. _Sandbox bugs that don't reproduce in prod waste your weekend._ `sk_test_*` and `sk_live_*` keys hit the same code paths. Same handlers. Same RLS policies. Same webhook dispatcher. The only difference is which database partition the data lands in. `cstar.isTestMode` returns true if the client was constructed with a test-prefixed key. Use it to gate noisy logs or to label dashboards — don't use it to branch around bugs. If something works in sandbox but breaks in live, the difference is your data, not our code. Start there. ```javascript const cstar = new CStarClient({ apiKey: process.env.CSTAR_KEY, // sk_test_... or sk_live_... teamId: process.env.CSTAR_TEAM_ID }); if (cstar.isTestMode) { console.log('🧪 Running against test data'); } ``` --- ### 04. Idempotency is yours, not ours, to set. _You know when a retry is the same logical operation. We don't._ Pass `idempotencyKey` to any mutation and the SDK forwards it as the `Idempotency-Key` header. We dedupe within the standard 24-hour window — second request with the same key returns the cached first response. Use it for anything triggered by a retry-able event: form submissions, webhook handlers, queue jobs. The key should be deterministic for the logical operation — `submission_${formId}_${userId}`, not a fresh UUID per attempt. No key passed? You get exactly-once semantics on the network and at-least-once semantics on retries. That's your call to make. ```javascript // Webhook handler — same delivery may retry. Same key, same outcome. app.post('/stripe-webhook', async (req, res) => { const event = req.body; await cstar.tickets.create( { title: `Stripe issue: ${event.type}`, priority: 'high' }, { idempotencyKey: `stripe_${event.id}` } ); res.status(200).end(); }); ``` --- ### 05. Real-time is the default. Polling is the safety net. _The first message after a tab wakes up should already be on the screen._ `ChatClient` opens an SSE connection on subscribe. When the network blinks, it falls back to polling at the configured interval, then resumes streaming once SSE reconnects. You don't have to choose. `cstar.realtime.on('ticket.*', handler)` does the same for the admin SDK. Server-Sent Events for the live stream, automatic reconnection with replay via `Last-Event-ID`. Need polling-only for a hostile network? Pass `realtime: false` to `ChatClient`. The handler signature is identical — your UI doesn't care which path delivered the message. ```javascript // Live ticket events into your dashboard const off = cstar.realtime.on('ticket.*', (event) => { console.log(event.type, event.data); }); // Tear down on logout off(); cstar.destroy(); ``` --- ### 06. Errors carry their own docs. _Catch by class, not by string. Strings change; classes don't._ Every error thrown by the SDK is a `CStarError` subclass: `CStarValidationError`, `CStarAuthenticationError`, `CStarRateLimitError`, `CStarNotFoundError`, `CStarConflictError`, `CStarPermissionError`, `CStarServerError`, `CStarFeatureNotConfiguredError`. They all carry `code`, `requestId`, `docUrl`, `statusCode`, and the original `message`. The `docUrl` deep-links to the right section of `/developers/errors`. Paste the `requestId` into a support ticket and we can find the call in seconds. `instanceof` works across every subpath — `/auth`, `/library`, `/community`, `/quickhelp` — because the class identity is registered on a `Symbol.for(...)` slot of `globalThis`. Bundle splits don't break the check. ```javascript import { CStarRateLimitError, CStarValidationError } from '@cstar.help/js'; try { await cstar.tickets.create(formData); } catch (e) { if (e instanceof CStarRateLimitError) { return retryAfter(e.retryAfter); // seconds, default 60 } if (e instanceof CStarValidationError) { return showFieldError(e.param, e.message); // e.param holds the offending field } // Any other CStarError — log the request ID and bail console.error('Failed', e.code, e.requestId, e.docUrl); throw e; } ``` --- ### 07. The widget is the demo. The SDK is the escape hatch. _Drop in the embed when you want a working chat in five minutes. Reach for the SDK when you want it to feel like yours._ Most teams should start with the chat widget. One script tag, branded to your team's colors, configured from Settings → Widget. Done. When the widget can't bend the way you need — a custom UI for a logged-in app, an in-product help center, an embedded assistant — graduate to `@cstar.help/js/chat`, `/library`, `/community`, `/quickhelp`, or `/proactive`. Same backend, your render layer. You don't have to pick one. Run the widget on marketing pages and the SDK in the app. They share the same data; customers don't notice the seam. Widget — copy, paste, ship. ```html ``` --- ### 08. Every response has a request ID. Use it. _"It's broken" without an ID is a guessing game. With one, it's a lookup._ Every response from the API carries `meta.requestId` in the body and `x-cstar-request-id` in the headers. The SDK captures both into `client.lastMeta` after every call. Errors expose it as `error.requestId`. When you log a failure, log the request ID. When you contact support, paste it. We index against it; finding the call takes a second instead of a half-hour. Webhook deliveries also carry `x-cstar-request-id` — set to the request ID of the API call that triggered the event. Trace from your dashboard click all the way through to your webhook handler with one string. ```javascript try { await cstar.tickets.create(payload); } catch (e) { // Log this. Always. log.error('ticket.create failed', { code: e.code, requestId: e.requestId, docUrl: e.docUrl }); throw e; } ``` --- ### 09. Customers see calm. Agents see the cape. _Two audiences, two surfaces. The professional one is the contract; the gamified one is the soul._ Anything a customer touches — the chat widget, the public library, the community forum, your custom UI built on the SDK — stays clean and professional. No XP popups, no boss music. Anything an agent touches — the dashboard, the side panel, the metrics board — is fully gamified. XP, achievements, boss battles, quests. Support work is a job worth showing up for. Build with that line in mind. The SDK lets you compose either side. Don't mix them by accident. --- ### 10. Gamification ships on, but every agent can turn it off. _Some agents want the cape. Some just want to work. Both should be first-class._ Two switches govern the game layer. Boring Mode is per-agent, in Settings → Preferences → Boring Mode. It hides every XP popup, boss bar, and achievement toast — the agent sees clean professional metrics instead. XP keeps accumulating in the background, so flipping it back on doesn't lose progress. Boss Battles is per-team, set in onboarding and editable in Settings → Features. Disable it and the team-wide boss feature is dormant — no Backlog Beast, no boss music, no shared HP bar. Individual XP and achievements still work for agents who want them. Both toggles are reachable via the REST API for headless dashboards: `PATCH /api/v1/teams/{teamId}/settings/feature-flags` with `{ "bossBattlesEnabled": false }` flips the team-level switch (requires `manage_settings`), and `PATCH /api/v1/teams/{teamId}/members/{memberId}/preferences` with `{ "boringMode": true }` flips the per-agent one (requires `manage_members`). Storage: `teams.boss_battles_enabled` and `agents.boring_mode`. --- ## Authentication ``` Authorization: Bearer sk_live_xxxxxxxxxxxxx ``` ### Key types - **Secret keys** (`sk_live_*`, `sk_test_*`): Full CRUD. 1000 req/hour. Server-side only — never ship in browser code. - **Publishable keys** (`pk_live_*`, `pk_test_*`): Read-only. 100 req/hour. Safe in browsers. Generate keys in Settings → API Keys, or via the CLI: `cstar keys create --name "My Key"`. ### Rate limit headers Every response includes: - `X-RateLimit-Limit`: Maximum requests in the window. - `X-RateLimit-Remaining`: Requests left in the current window. - `X-RateLimit-Reset`: Unix timestamp when the window resets. - `X-RateLimit-Window`: Window duration in seconds (defaults to 3600). ### Test mode is real mode `sk_test_*` keys hit the same code paths as `sk_live_*`. Only the data is partitioned. The `isTestMode` getter on the client flips based on the prefix. --- ## Base URL ``` https://www.cstar.help/api/v1/teams/{teamId} ``` `{teamId}` is your team's UUID — find it in Settings → General → Team ID, or via `cstar status`. --- ## Response shape ```json { "success": true, "data": { ... }, "meta": { "requestId": "req_abc123", "timestamp": "2026-04-27T18:42:11.000Z" } } ``` List endpoints add `meta.pagination`: ```json { "meta": { "requestId": "req_abc123", "timestamp": "2026-04-27T18:42:11.000Z", "pagination": { "page": 1, "pageSize": 20, "total": 142, "totalPages": 8 } } } ``` ### Error shape ```json { "success": false, "error": { "type": "validation_error", "code": "INVALID_FIELD", "message": "Title is required", "field": "title", "doc_url": "https://www.cstar.help/developers/api-reference/tickets" } } ``` ### HTTP status codes - 200: Success - 201: Created - 400: Bad request (validation error) - 401: Unauthorized (invalid or missing API key) - 403: Forbidden (wrong key type or team access) - 404: Not found - 429: Rate limited - 500: Server error ### Request IDs Every response carries the `x-cstar-request-id` header AND `meta.requestId`. The SDK captures both into `client.lastMeta` after every call. `CStarError` instances expose `.requestId`. When you contact support, paste the request ID — that's the fastest way to find your call. --- ## API Reference ### Tickets Base path: `/api/v1/teams/{teamId}/tickets` Tickets are the core of cStar — each one represents a customer support conversation. Create, update, and resolve tickets through the API. #### Tickets object - **id** (string): Unique ticket identifier (tkt_ prefix) - **title** (string): Ticket subject line - **status** (string): Current status Values: new, open, pending, resolved, closed - **priority** (string): Priority level Values: low, normal, high, urgent - **customerId** (string): Associated customer ID (cus_ prefix) - **assignedTo** (string): Assigned agent ID - **tags** (string[]): Array of tag strings - **notes** (string): Internal notes (not visible to customers) - **messageCount** (integer): Number of messages on this ticket - **createdAt** (datetime): When the ticket was created - **updatedAt** (datetime): Last modification timestamp - **resolvedAt** (datetime): When the ticket was resolved (if applicable) #### Endpoints ##### GET /api/v1/teams/{teamId}/tickets Retrieve a paginated list of tickets with optional filtering by status, priority, customer, or search term. Query parameters: - **status** (string): Filter by status Values: new, open, pending, resolved, closed - **priority** (string): Filter by priority Values: low, normal, high, urgent - **search** (string): Full-text search in title and content - **customerId** (string): Filter tickets by customer - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript import { CStarClient } from '@cstar.help/js'; const cstar = new CStarClient({ apiKey: 'sk_live_your_key' }); const { data, meta } = await cstar.tickets.list({ status: 'open', page: 1 }); ``` Response (200): ```json { "success": true, "data": [ { "id": "tkt_550e8400e29b41d4a716446655440000", "title": "Cannot log in to my account", "status": "open", "priority": "high", "customerId": "cus_7c9e667974254de0944be07fc1f90ae7", "tags": [ "login", "authentication" ], "messageCount": 3, "createdAt": "2025-12-10T14:30:00Z" } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z", "pagination": { "page": 1, "pageSize": 20, "total": 142, "totalPages": 8 } } } ``` ##### GET /api/v1/teams/{teamId}/tickets/{ticketId} Retrieve a single ticket by ID. Includes the full message thread. Path parameters: - **ticketId** (string): The ticket ID (tkt_ prefix) JavaScript example: ```javascript const { data: ticket } = await cstar.tickets.get('tkt_550e8400e29b41d4a716446655440000'); console.log(ticket.title, ticket.messages.length); ``` Response (200): ```json { "success": true, "data": { "id": "tkt_550e8400e29b41d4a716446655440000", "title": "Cannot log in to my account", "status": "open", "priority": "high", "customerId": "cus_7c9e667974254de0944be07fc1f90ae7", "assignedTo": null, "tags": [ "login", "authentication" ], "notes": null, "messageCount": 3, "messages": [ { "id": "msg_a1b2c3d4e5f6", "content": "I keep getting \"invalid credentials\" when I try to log in.", "sender": "customer", "senderName": "Jane Smith", "createdAt": "2025-12-10T14:30:00Z" } ], "createdAt": "2025-12-10T14:30:00Z", "updatedAt": "2025-12-10T15:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T15:00:00Z" } } ``` ##### POST /api/v1/teams/{teamId}/tickets Create a new support ticket. Optionally attach it to an existing customer or provide customer details inline. > Requires a secret API key. Body parameters: - **title** (string) (required): Ticket subject line - **priority** (string): Priority level Values: low, normal, high, urgent - **customerId** (string): Existing customer ID (cus_ prefix) - **customerName** (string): Customer name (used if customerId is not provided) - **tags** (string[]): Array of tag strings. Also accepts comma-separated string via curl. - **notes** (string): Internal notes - **metadata** (object): Arbitrary key-value metadata (max 50 keys). On update, keys are merged — set a key to null to remove it. JavaScript example: ```javascript const { data: ticket } = await cstar.tickets.create({ title: 'Cannot log in to my account', priority: 'high', customerName: 'Jane Smith', tags: 'login,authentication' }); ``` Response (201): ```json { "success": true, "data": { "id": "tkt_550e8400e29b41d4a716446655440000", "title": "Cannot log in to my account", "status": "new", "priority": "high", "tags": [ "login", "authentication" ], "createdAt": "2025-12-10T14:30:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### PATCH /api/v1/teams/{teamId}/tickets/{ticketId} Update one or more fields on an existing ticket. Only include the fields you want to change. > Requires a secret API key. Path parameters: - **ticketId** (string): The ticket ID (tkt_ prefix) Body parameters: - **title** (string): New title - **status** (string): New status Values: new, open, pending, resolved, closed - **priority** (string): New priority Values: low, normal, high, urgent - **tags** (string[]): Array of tag strings (replaces existing). Also accepts comma-separated string via curl. - **notes** (string): Internal notes - **metadata** (object): Key-value metadata to merge. Existing keys preserved, new keys added. Set a key to null to remove it. JavaScript example: ```javascript const { data: updated } = await cstar.tickets.update( 'tkt_550e8400e29b41d4a716446655440000', { status: 'resolved', notes: 'Password reset sent' } ); ``` Response (200): ```json { "success": true, "data": { "id": "tkt_550e8400e29b41d4a716446655440000", "title": "Cannot log in to my account", "status": "resolved", "priority": "high", "updatedAt": "2025-12-10T16:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T16:00:00Z" } } ``` ##### DELETE /api/v1/teams/{teamId}/tickets/{ticketId} Permanently delete a ticket and all its messages. This action cannot be undone. > Requires a secret API key. This is a destructive operation. Path parameters: - **ticketId** (string): The ticket ID (tkt_ prefix) JavaScript example: ```javascript await cstar.tickets.del('tkt_550e8400e29b41d4a716446655440000'); ``` Response (200): ```json { "success": true, "data": { "deleted": true }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` --- ### Customers Base path: `/api/v1/teams/{teamId}/customers` Customers represent the people who submit tickets. Track their history, sentiment, and metadata across all interactions. #### Customers object - **id** (string): Unique customer identifier (cus_ prefix) - **name** (string): Full name - **email** (string): Email address - **status** (string): Account status Values: active, inactive - **sentiment** (string): Calculated sentiment based on interactions Values: positive, neutral, negative - **tags** (string[]): Array of tag strings - **notes** (string): Internal notes about this customer - **ticketCount** (integer): Total number of tickets - **createdAt** (datetime): When the customer was created - **updatedAt** (datetime): Last modification timestamp #### Endpoints ##### GET /api/v1/teams/{teamId}/customers Retrieve a paginated list of customers with optional filtering. Query parameters: - **status** (string): Filter by status Values: active, inactive - **sentiment** (string): Filter by sentiment Values: positive, neutral, negative - **search** (string): Search by name or email - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const { data, meta } = await cstar.customers.list({ status: 'active', search: 'jane' }); ``` Response (200): ```json { "success": true, "data": [ { "id": "cus_7c9e667974254de0944be07fc1f90ae7", "name": "Jane Smith", "email": "jane@example.com", "status": "active", "sentiment": "positive", "ticketCount": 5, "createdAt": "2025-11-01T10:00:00Z" } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z", "pagination": { "page": 1, "pageSize": 20, "total": 142, "totalPages": 8 } } } ``` ##### GET /api/v1/teams/{teamId}/customers/{customerId} Retrieve a single customer by ID with their full profile and ticket history summary. Path parameters: - **customerId** (string): The customer ID (cus_ prefix) JavaScript example: ```javascript const { data: customer } = await cstar.customers.get( 'cus_7c9e667974254de0944be07fc1f90ae7' ); ``` Response (200): ```json { "success": true, "data": { "id": "cus_7c9e667974254de0944be07fc1f90ae7", "name": "Jane Smith", "email": "jane@example.com", "status": "active", "sentiment": "positive", "tags": [ "vip", "enterprise" ], "notes": "Key account — escalate issues immediately", "ticketCount": 5, "createdAt": "2025-11-01T10:00:00Z", "updatedAt": "2025-12-10T14:30:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### POST /api/v1/teams/{teamId}/customers Create a new customer record. Email must be unique within your team. > Requires a secret API key. Body parameters: - **name** (string) (required): Full name - **email** (string) (required): Email address - **tags** (string[]): Array of tag strings. Also accepts comma-separated string via curl. - **notes** (string): Internal notes - **metadata** (object): Arbitrary key-value metadata (max 50 keys). On update, keys are merged — set a key to null to remove it. JavaScript example: ```javascript const { data: customer } = await cstar.customers.create({ name: 'Jane Smith', email: 'jane@example.com', tags: 'vip,enterprise' }); ``` Response (201): ```json { "success": true, "data": { "id": "cus_7c9e667974254de0944be07fc1f90ae7", "name": "Jane Smith", "email": "jane@example.com", "status": "active", "createdAt": "2025-12-10T14:30:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### PATCH /api/v1/teams/{teamId}/customers/{customerId} Update one or more fields on an existing customer. > Requires a secret API key. Path parameters: - **customerId** (string): The customer ID (cus_ prefix) Body parameters: - **name** (string): Full name - **email** (string): Email address - **status** (string): Account status Values: active, inactive - **sentiment** (string): Override sentiment Values: positive, neutral, negative - **tags** (string[]): Array of tag strings (replaces existing). Also accepts comma-separated string via curl. - **notes** (string): Internal notes - **metadata** (object): Key-value metadata to merge. Existing keys preserved, new keys added. Set a key to null to remove it. JavaScript example: ```javascript const { data: updated } = await cstar.customers.update(customerId, { tags: 'vip,enterprise,priority' }); ``` Response (200): ```json { "success": true, "data": { "id": "cus_7c9e667974254de0944be07fc1f90ae7", "name": "Jane Smith", "status": "active", "updatedAt": "2025-12-10T16:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T16:00:00Z" } } ``` ##### DELETE /api/v1/teams/{teamId}/customers/{customerId} Permanently delete a customer. Associated tickets are preserved but unlinked. > Requires a secret API key. This is a destructive operation. Path parameters: - **customerId** (string): The customer ID (cus_ prefix) JavaScript example: ```javascript await cstar.customers.del(customerId); ``` Response (200): ```json { "success": true, "data": { "deleted": true }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` --- ### Customer Groups Base path: `/api/v1/teams/{teamId}/customer-groups` Customer groups let you organize customers into flexible categories — like Zendesk organizations, Intercom companies, or custom segments. Customers can belong to multiple groups. #### Customer Groups object - **id** (string): Unique group identifier - **name** (string): Group name - **slug** (string): URL-friendly slug (auto-generated from name if omitted) - **description** (string): Optional description - **externalId** (string): External platform ID (for import matching — e.g., Zendesk org ID) - **customFields** (object): Arbitrary key-value metadata - **customerCount** (integer): Number of customers in this group - **createdAt** (datetime): When the group was created - **updatedAt** (datetime): Last modification timestamp #### Endpoints ##### GET /api/v1/teams/{teamId}/customer-groups Retrieve a paginated list of customer groups. Query parameters: - **search** (string): Search groups by name - **page** (integer): Page number (default: 1) - **pageSize** (integer): Results per page (default: 25, max: 100) JavaScript example: ```javascript const { data } = await cstar.customerGroups.list(); ``` Response (200): ```json { "data": [ { "id": "a1b2c3d4-...", "object": "customer_group", "name": "Acme Corp", "slug": "acme-corp", "description": "Enterprise tier customer", "customerCount": 42, "createdAt": "2025-01-15T10:00:00Z" } ], "pagination": { "total": 15, "page": 1, "pageSize": 25, "hasMore": false } } ``` ##### POST /api/v1/teams/{teamId}/customer-groups Create a new customer group. > Requires a secret API key. Body parameters: - **name** (string) (required): Group name - **slug** (string): URL-friendly slug (auto-generated if omitted) - **description** (string): Optional description - **externalId** (string): External platform ID for import matching - **customFields** (object): Arbitrary key-value metadata JavaScript example: ```javascript const group = await cstar.customerGroups.create({ name: 'Acme Corp' }); ``` Response (201): ```json { "id": "a1b2c3d4-...", "object": "customer_group", "name": "Acme Corp", "slug": "acme-corp", "customerCount": 0, "createdAt": "2025-01-15T10:00:00Z" } ``` ##### GET /api/v1/teams/{teamId}/customer-groups/{groupId} Retrieve a single customer group by ID. Path parameters: - **groupId** (string): Customer group ID JavaScript example: ```javascript const group = await cstar.customerGroups.get(groupId); ``` Response (200): ```json { "id": "a1b2c3d4-...", "object": "customer_group", "name": "Acme Corp", "slug": "acme-corp", "description": "Enterprise tier customer", "customFields": { "tier": "enterprise", "region": "us-west" }, "customerCount": 42, "createdAt": "2025-01-15T10:00:00Z" } ``` ##### PATCH /api/v1/teams/{teamId}/customer-groups/{groupId} Update a customer group. Only provided fields are changed. > Requires a secret API key. Path parameters: - **groupId** (string): Customer group ID Body parameters: - **name** (string): Group name - **slug** (string): URL-friendly slug - **description** (string): Description - **customFields** (object): Custom metadata JavaScript example: ```javascript const group = await cstar.customerGroups.update(groupId, { name: 'Acme Corp Updated' }); ``` Response (200): ```json { "id": "a1b2c3d4-...", "object": "customer_group", "name": "Acme Corp Updated" } ``` ##### DELETE /api/v1/teams/{teamId}/customer-groups/{groupId} Delete a customer group. Members are removed from the group but not deleted. > Requires a secret API key. Path parameters: - **groupId** (string): Customer group ID JavaScript example: ```javascript await cstar.customerGroups.del(groupId); ``` Response (200): ```json { "deleted": true, "id": "a1b2c3d4-..." } ``` ##### GET /api/v1/teams/{teamId}/customer-groups/{groupId}/members List customers that belong to a group. Path parameters: - **groupId** (string): Customer group ID Query parameters: - **page** (integer): Page number (default: 1) - **pageSize** (integer): Results per page (default: 25) JavaScript example: ```javascript const { data } = await cstar.customerGroups.listMembers(groupId); ``` Response (200): ```json { "data": [ { "id": "cus_xxx", "object": "customer", "name": "Rick Astley", "email": "rick@acme.com", "joinedGroupAt": "2025-01-15T10:00:00Z" } ], "pagination": { "total": 42, "page": 1, "pageSize": 25, "hasMore": true } } ``` ##### POST /api/v1/teams/{teamId}/customer-groups/{groupId}/members Add one or more customers to a group. Duplicates are silently skipped. > Requires a secret API key. Path parameters: - **groupId** (string): Customer group ID Body parameters: - **customerId** (string): Single customer ID to add - **customerIds** (string[]): Array of customer IDs to add (max 100). Use this OR customerId. JavaScript example: ```javascript await cstar.customerGroups.addMembers(groupId, { customerIds: ['cus_xxx'] }); ``` Response (201): ```json { "added": 3, "total": 5, "skipped": 2 } ``` ##### DELETE /api/v1/teams/{teamId}/customer-groups/{groupId}/members Remove a customer from a group. > Requires a secret API key. Path parameters: - **groupId** (string): Customer group ID Query parameters: - **customerId** (string): Customer ID to remove JavaScript example: ```javascript await cstar.customerGroups.removeMember(groupId, customerId); ``` Response (200): ```json { "removed": true, "customerId": "cus_xxx", "groupId": "a1b2c3d4-..." } ``` --- ### Articles Base path: `/api/v1/teams/{teamId}/articles` Articles power your knowledge base. Create, publish, and manage help content that customers and agents can search. #### Articles object - **id** (string): Unique article identifier (art_ prefix) - **title** (string): Article title - **content** (string): Article body (Markdown) - **category** (string): Category name - **status** (string): Publication status Values: draft, published - **isPublic** (boolean): Whether visible in public knowledge base - **tags** (string[]): Array of tag strings - **viewCount** (integer): Number of times viewed - **useCount** (integer): Number of times used in replies - **createdAt** (datetime): When the article was created - **updatedAt** (datetime): Last modification timestamp #### Endpoints ##### GET /api/v1/teams/{teamId}/articles Retrieve a paginated list of knowledge base articles with optional filtering. Query parameters: - **category** (string): Filter by category name - **status** (string): Filter by publication status Values: draft, published - **isPublic** (boolean): Filter by public visibility - **search** (string): Full-text search in title and content - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const { data } = await cstar.articles.list({ status: 'published', category: 'Account' }); ``` Response (200): ```json { "success": true, "data": [ { "id": "art_a1b2c3d4e5f67890abcdef1234567890", "title": "How to reset your password", "category": "Account", "status": "published", "isPublic": true, "viewCount": 342, "useCount": 28, "createdAt": "2025-11-15T09:00:00Z" } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z", "pagination": { "page": 1, "pageSize": 20, "total": 142, "totalPages": 8 } } } ``` ##### GET /api/v1/teams/{teamId}/articles/{articleId} Retrieve a single article by ID with full content. Path parameters: - **articleId** (string): The article ID (art_ prefix) JavaScript example: ```javascript const { data: article } = await cstar.articles.get(articleId); ``` Response (200): ```json { "success": true, "data": { "id": "art_a1b2c3d4e5f67890abcdef1234567890", "title": "How to reset your password", "content": "# Password Reset\n\n1. Go to login page\n2. Click \"Forgot Password\"...", "category": "Account", "status": "published", "isPublic": true, "tags": [ "password", "account", "security" ], "viewCount": 342, "useCount": 28, "createdAt": "2025-11-15T09:00:00Z", "updatedAt": "2025-12-01T11:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### POST /api/v1/teams/{teamId}/articles Create a new knowledge base article. Articles start as drafts by default. > Requires a secret API key. Body parameters: - **title** (string) (required): Article title - **content** (string): Article body (Markdown) - **category** (string): Category name - **status** (string): Publication status Values: draft, published - **isPublic** (boolean): Whether visible publicly - **tags** (string[]): Array of tag strings. Also accepts comma-separated string via curl. - **metadata** (object): Arbitrary key-value metadata (max 50 keys) JavaScript example: ```javascript const { data: article } = await cstar.articles.create({ title: 'Getting Started Guide', content: '# Welcome\n\nHere is how to get started...', category: 'Onboarding' }); ``` Response (201): ```json { "success": true, "data": { "id": "art_a1b2c3d4e5f67890abcdef1234567890", "title": "Getting Started Guide", "status": "draft", "isPublic": false, "createdAt": "2025-12-10T14:30:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### PATCH /api/v1/teams/{teamId}/articles/{articleId} Update one or more fields on an existing article. > Requires a secret API key. Path parameters: - **articleId** (string): The article ID (art_ prefix) Body parameters: - **title** (string): Article title - **content** (string): Article body (Markdown) - **category** (string): Category name - **status** (string): Publication status Values: draft, published - **isPublic** (boolean): Whether visible publicly - **tags** (string[]): Array of tag strings. Also accepts comma-separated string via curl. - **metadata** (object): Key-value metadata to merge. Existing keys preserved, new keys added. Set a key to null to remove it. JavaScript example: ```javascript const { data: updated } = await cstar.articles.update(articleId, { status: 'published', isPublic: true }); ``` Response (200): ```json { "success": true, "data": { "id": "art_a1b2c3d4e5f67890abcdef1234567890", "title": "Getting Started Guide", "status": "published", "updatedAt": "2025-12-10T16:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T16:00:00Z" } } ``` ##### DELETE /api/v1/teams/{teamId}/articles/{articleId} Permanently delete an article from the knowledge base. > Requires a secret API key. This is a destructive operation. Path parameters: - **articleId** (string): The article ID (art_ prefix) JavaScript example: ```javascript await cstar.articles.del(articleId); ``` Response (200): ```json { "success": true, "data": { "deleted": true }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` --- ### Messages Base path: `/api/v1/teams/{teamId}/tickets/{ticketId}/messages` Messages belong to tickets and represent the conversation thread between agents and customers. Each message has a sender type and optional metadata. #### Messages object - **id** (string): Unique message identifier (msg_ prefix) - **ticketId** (string): Parent ticket ID (tkt_ prefix) - **content** (string): Message body text - **sender** (string): Who sent the message Values: agent, customer, system - **senderName** (string): Display name of the sender - **isInternal** (boolean): Whether this is an internal note (not visible to customers) - **createdAt** (datetime): When the message was sent #### Endpoints ##### GET /api/v1/teams/{teamId}/tickets/{ticketId}/messages Get all messages for a ticket, ordered chronologically. Path parameters: - **ticketId** (string): The parent ticket ID (tkt_ prefix) JavaScript example: ```javascript const { data: messages } = await cstar.tickets.messages.list(ticketId); ``` Response (200): ```json { "success": true, "data": [ { "id": "msg_a1b2c3d4e5f6", "ticketId": "tkt_550e8400e29b41d4a716446655440000", "content": "I keep getting \"invalid credentials\" when I try to log in.", "sender": "customer", "senderName": "Jane Smith", "isInternal": false, "createdAt": "2025-12-10T14:30:00Z" }, { "id": "msg_b2c3d4e5f6a7", "ticketId": "tkt_550e8400e29b41d4a716446655440000", "content": "I'll look into this for you right away.", "sender": "agent", "senderName": "Bob (Support)", "isInternal": false, "createdAt": "2025-12-10T14:35:00Z" } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:35:00Z" } } ``` ##### POST /api/v1/teams/{teamId}/tickets/{ticketId}/messages Add a new message to a ticket thread. Fires the `ticket.message_added` webhook event. > Requires a secret API key. Path parameters: - **ticketId** (string): The parent ticket ID (tkt_ prefix) Body parameters: - **content** (string) (required): Message body text - **sender** (string): Sender type Values: agent, customer, system - **senderName** (string): Display name (optional) - **isInternal** (boolean): Mark as internal note (not visible to customers) JavaScript example: ```javascript const { data: message } = await cstar.tickets.messages.create(ticketId, { content: "I've reset your password. Please try again.", sender: 'agent' }); ``` Response (201): ```json { "success": true, "data": { "id": "msg_c3d4e5f6a7b8", "ticketId": "tkt_550e8400e29b41d4a716446655440000", "content": "I've reset your password. Please try logging in again.", "sender": "agent", "senderName": "Bob (Support)", "isInternal": false, "createdAt": "2025-12-10T14:45:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:45:00Z" } } ``` --- ### Webhooks Base path: `/api/v1/teams/{teamId}/webhooks` Webhooks notify your application in real-time when events happen in cStar. Subscribe to ticket, customer, article, and gamification events. #### Webhooks object - **id** (string): Unique webhook identifier (whk_ prefix) - **name** (string): Friendly name for the webhook - **url** (string): Delivery URL (HTTPS) - **events** (string[]): Array of subscribed event types - **secret** (string): HMAC signing secret (whsec_ prefix, shown once at creation) - **isActive** (boolean): Whether the webhook is currently active - **createdAt** (datetime): When the webhook was created - **updatedAt** (datetime): Last modification timestamp #### Endpoints ##### GET /api/v1/teams/{teamId}/webhooks Retrieve all configured webhooks for your team. JavaScript example: ```javascript const { data: webhooks } = await cstar.webhooks.list(); ``` Response (200): ```json { "success": true, "data": [ { "id": "whk_a1b2c3d4e5f6", "name": "Slack Notifications", "url": "https://hooks.slack.com/services/...", "events": [ "ticket.created", "ticket.closed" ], "isActive": true, "createdAt": "2025-11-01T10:00:00Z" } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### GET /api/v1/teams/{teamId}/webhooks/{webhookId} Retrieve a single webhook by ID. Path parameters: - **webhookId** (string): The webhook ID (whk_ prefix) JavaScript example: ```javascript const { data: webhook } = await cstar.webhooks.get(webhookId); ``` Response (200): ```json { "success": true, "data": { "id": "whk_a1b2c3d4e5f6", "name": "Slack Notifications", "url": "https://hooks.slack.com/services/...", "events": [ "ticket.created", "ticket.closed" ], "isActive": true, "createdAt": "2025-11-01T10:00:00Z", "updatedAt": "2025-11-15T12:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### POST /api/v1/teams/{teamId}/webhooks Create a new webhook subscription. The signing secret is returned once — save it immediately. > Requires a secret API key. Save the returned secret — it cannot be retrieved again. Body parameters: - **name** (string) (required): Friendly name - **url** (string) (required): Delivery URL (must be HTTPS) - **events** (string) (required): Comma-separated event types to subscribe to JavaScript example: ```javascript const { data: webhook } = await cstar.webhooks.create({ name: 'My Webhook', url: 'https://example.com/webhook', events: 'ticket.created,ticket.closed' }); // Save webhook.secret — shown only once! console.log('Secret:', webhook.secret); ``` Response (201): ```json { "success": true, "data": { "id": "whk_b2c3d4e5f6a7", "name": "My Webhook", "url": "https://example.com/webhook", "events": [ "ticket.created", "ticket.closed" ], "secret": "whsec_AbCdEf1234567890...", "isActive": true, "createdAt": "2025-12-10T14:30:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### PATCH /api/v1/teams/{teamId}/webhooks/{webhookId} Update an existing webhook. Use this to change the URL, events, or toggle active status. > Requires a secret API key. Path parameters: - **webhookId** (string): The webhook ID (whk_ prefix) Body parameters: - **name** (string): Friendly name - **url** (string): Delivery URL (must be HTTPS) - **events** (string): Comma-separated event types - **isActive** (boolean): Enable or disable the webhook JavaScript example: ```javascript const { data: updated } = await cstar.webhooks.update(webhookId, { events: 'ticket.created,ticket.updated,ticket.closed' }); ``` Response (200): ```json { "success": true, "data": { "id": "whk_b2c3d4e5f6a7", "name": "My Webhook", "url": "https://example.com/webhook", "events": [ "ticket.created", "ticket.updated", "ticket.closed" ], "isActive": true, "updatedAt": "2025-12-10T16:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T16:00:00Z" } } ``` ##### DELETE /api/v1/teams/{teamId}/webhooks/{webhookId} Permanently delete a webhook subscription. > Requires a secret API key. Path parameters: - **webhookId** (string): The webhook ID (whk_ prefix) JavaScript example: ```javascript await cstar.webhooks.del(webhookId); ``` Response (200): ```json { "success": true, "data": { "deleted": true }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### POST /api/v1/teams/{teamId}/webhooks/trigger-test Fire a test webhook event to all active webhooks and CLI listeners. Useful for testing your integration. > Requires a secret API key. Also available via CLI: `cstar trigger ` Body parameters: - **event** (string) (required): Event type to fire Values: ticket.created, ticket.updated, ticket.closed, ticket.message_added, customer.created, customer.updated, article.created, article.published, article.updated, boss.spawned, boss.defeated, player.level_up JavaScript example: ```javascript await cstar.webhooks.test(webhookId, { event: 'ticket.created' }); ``` Response (200): ```json { "success": true, "data": { "event": "ticket.created", "eventId": "evt_abc123", "test": true, "deliveries": [] }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` --- ### Categories Base path: `/api/v1/teams/{teamId}/categories` Categories organize your knowledge base articles into logical groups. Customers see public categories when browsing your help center. #### Categories object - **id** (string): Unique category identifier (UUID) - **name** (string): Category display name - **slug** (string): URL-safe slug (auto-generated from name) - **description** (string): Category description - **icon** (string): Icon identifier - **color** (string): Color value - **sortOrder** (integer): Display order (ascending) - **isPublic** (boolean): Whether visible in the public knowledge base - **createdAt** (datetime): When the category was created - **updatedAt** (datetime): Last modification timestamp #### Endpoints ##### GET /api/v1/teams/{teamId}/categories Retrieve all categories for a team, ordered by sort order then name. Query parameters: - **isPublic** (boolean): Filter to public categories only JavaScript example: ```javascript const { data } = await cstar.categories.list(); console.log(data); // [{ id, name, slug, ... }] ``` Response (200): ```json { "success": true, "data": [ { "id": "7134fc12-0302-4e58-9466-a770035d5da9", "object": "category", "name": "Getting Started", "slug": "getting-started", "description": "Beginner guides and onboarding", "isPublic": true, "sortOrder": 0, "createdAt": "2025-12-10T14:30:00Z" } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z", "pagination": { "total": 3, "page": 1, "pageSize": 3, "hasMore": false } } } ``` ##### POST /api/v1/teams/{teamId}/categories Create a new article category. Slug is auto-generated from the name. > Requires a secret API key. Body parameters: - **name** (string) (required): Category display name - **description** (string): Category description - **icon** (string): Icon identifier - **color** (string): Color value - **sortOrder** (integer): Display order - **isPublic** (boolean): Whether visible publicly JavaScript example: ```javascript const { data } = await cstar.categories.create({ name: 'Getting Started', isPublic: true, }); ``` Response (201): ```json { "success": true, "data": { "id": "7134fc12-0302-4e58-9466-a770035d5da9", "object": "category", "name": "Getting Started", "slug": "getting-started", "description": null, "isPublic": false, "sortOrder": 0, "createdAt": "2025-12-10T14:30:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` ##### PATCH /api/v1/teams/{teamId}/categories/{categoryId} Update one or more fields on an existing category. Only include the fields you want to change. > Requires a secret API key. Path parameters: - **categoryId** (string): The category ID (UUID) Body parameters: - **name** (string): New name - **description** (string): New description - **icon** (string): New icon - **color** (string): New color - **sortOrder** (integer): New sort order - **isPublic** (boolean): New visibility JavaScript example: ```javascript const { data } = await cstar.categories.update(categoryId, { description: 'Updated description', isPublic: true, }); ``` Response (200): ```json { "success": true, "data": { "id": "7134fc12-0302-4e58-9466-a770035d5da9", "object": "category", "name": "Getting Started", "slug": "getting-started", "description": "Updated description", "isPublic": true, "updatedAt": "2025-12-10T16:00:00Z" }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T16:00:00Z" } } ``` ##### DELETE /api/v1/teams/{teamId}/categories/{categoryId} Permanently delete a category. Articles in this category will become uncategorized. > Requires a secret API key. This is a destructive operation. Path parameters: - **categoryId** (string): The category ID (UUID) JavaScript example: ```javascript await cstar.categories.del(categoryId); ``` Response (200): ```json { "success": true, "data": { "deleted": true }, "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z" } } ``` --- ### Community Base path: `/api/v1/teams/{teamId}/community` Community posts let customers ask questions, share feedback, and vote on feature requests. Agents can manage posts, set statuses, and moderate content. #### Community object - **id** (uuid): Unique post identifier - **title** (string): Post title - **slug** (string): URL-friendly slug - **body** (string): Post body text - **topic_id** (uuid): Topic category ID - **status** (string): Post status Values: open, answered, officially_answered, planned, not_planned, completed, under_review - **visibility** (string): Post visibility Values: public, members_only - **vote_count** (integer): Number of upvotes - **comment_count** (integer): Number of comments - **is_pinned** (boolean): Whether post is pinned to top - **is_locked** (boolean): Whether new comments are disabled - **custom_fields** (object): Custom field values as key-value pairs (field definition ID → value). Only fields assigned to the post's topic are accepted. Internal only — not returned by public API. - **attachments** (array): Image attachments (max 10 per post, 5 per comment). Each object: {filename, content_type, size_bytes, storage_path}. Images only (JPEG, PNG, GIF, WebP), 5MB max per file. - **created_at** (datetime): When the post was created #### Endpoints ##### GET /api/v1/teams/{teamId}/community/posts Get all community posts for the team with optional filters. Query parameters: - **topic** (string): Filter by topic slug - **status** (string): Filter by status - **sort** (string) (default: recent): Sort order: recent, votes, comments - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const posts = await cstar.community.getPosts({ sort: 'votes' }); ``` Response (200): ```json { "success": true, "data": [ { "id": "...", "title": "Add dark mode", "status": "planned", "vote_count": 42 } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z", "pagination": { "page": 1, "pageSize": 20, "total": 142, "totalPages": 8 } } } ``` ##### POST /api/v1/teams/{teamId}/community/posts Create a new community post. Body parameters: - **title** (string) (required): Post title - **body** (string): Post body text - **topicId** (uuid) (required): Topic category ID - **visibility** (string): public or members_only - **customFields** (object): Custom field values as {fieldDefinitionId: value}. Only fields assigned to the topic are accepted. Required fields (per topic config) must be included. - **attachments** (array): Image attachments (max 10). Each: {filename, content_type, size_bytes, storage_path}. Images only, 5MB max each. JavaScript example: ```javascript const post = await cstar.community.createPost({ title: 'Tutorial Video', topicId: 'topic-uuid', customFields: { 'field-def-id': 'https://youtube.com/watch?v=abc' } }); ``` Response (201): ```json { "success": true, "data": { "id": "...", "title": "New feature idea", "status": "open" } } ``` ##### PATCH /api/v1/teams/{teamId}/community/posts/{postId} Update a community post status or properties. Path parameters: - **postId** (uuid): Post ID Body parameters: - **status** (string): New status (open, planned, completed, etc.) - **is_pinned** (boolean): Pin to top of topic - **is_locked** (boolean): Lock comments - **customFields** (object): Custom field values as {fieldDefinitionId: value}. Merged with existing values. Required fields cannot be removed. - **attachments** (array): Replace post attachments (images only, max 10, 5MB each) JavaScript example: ```javascript await cstar.community.updatePost(postId, { status: 'planned', customFields: { 'field-id': 'new-value' } }); ``` Response (200): ```json { "success": true, "data": { "id": "...", "status": "planned" } } ``` ##### GET /api/v1/teams/{teamId}/community/topics Get all community topics for the team. Each topic includes a customFieldConfig array specifying which custom fields appear on posts in that topic. JavaScript example: ```javascript const topics = await cstar.community.topics.list(); ``` Response (200): ```json { "success": true, "data": [ { "id": "...", "name": "Videos", "slug": "videos", "customFieldConfig": [ { "fieldId": "field-def-id", "required": true } ], "postCount": 12 } ] } ``` --- ### Members Base path: `/api/v1/teams/{teamId}/members` Team members are the agents and admins who use cStar. Manage roles, view stats, and send invites. #### Members object - **id** (string): Membership ID - **userId** (string): User account ID - **name** (string): Display name - **email** (string): Email address - **avatarUrl** (string): Avatar image URL - **role** (string): Team role Values: owner, admin, manager, librarian, agent, spectator - **roleDisplayName** (string): Game title for role (e.g., The Hero) - **permissions** (string[]): Array of permission keys - **level** (integer): Player level - **xp** (integer): Current XP - **joinedAt** (datetime): When the member joined #### Endpoints ##### GET /api/v1/teams/{teamId}/members List all team members with their roles, levels, and permissions. Query parameters: - **role** (string): Filter by role Values: owner, admin, manager, librarian, agent, spectator - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const { data: members } = await cstar.members.list(); ``` Response (200): ```json { "success": true, "data": [ { "id": "mem_abc123", "name": "Jane Smith", "email": "jane@team.com", "role": "agent", "roleDisplayName": "The Hero", "level": 12, "xp": 4850, "joinedAt": "2025-06-15T10:00:00Z" } ], "meta": { "requestId": "req_abc123", "timestamp": "2025-12-10T14:30:00Z", "pagination": { "page": 1, "pageSize": 20, "total": 142, "totalPages": 8 } } } ``` ##### GET /api/v1/teams/{teamId}/members/{memberId} Get a single team member by membership ID. Path parameters: - **memberId** (string): Membership ID JavaScript example: ```javascript const { data: member } = await cstar.members.get('mem_abc123'); ``` Response (200): ```json { "success": true, "data": { "id": "mem_abc123", "name": "Jane Smith", "role": "agent", "level": 12 } } ``` ##### PATCH /api/v1/teams/{teamId}/members/{memberId} Update a member's role. Cannot change the owner role. Path parameters: - **memberId** (string): Membership ID Body parameters: - **role** (string): New role Values: admin, manager, librarian, agent, spectator JavaScript example: ```javascript await cstar.members.update('mem_abc123', { role: 'manager' }); ``` Response (200): ```json { "success": true, "data": { "id": "mem_abc123", "role": "manager" } } ``` ##### DELETE /api/v1/teams/{teamId}/members/{memberId} Remove a member from the team. Cannot remove the team owner. Path parameters: - **memberId** (string): Membership ID JavaScript example: ```javascript await cstar.members.del('mem_abc123'); ``` Response (200): ```json { "success": true, "data": { "deleted": true, "id": "mem_abc123" } } ``` ##### GET /api/v1/teams/{teamId}/members/../invites List pending team invites. > Actual path: /api/v1/teams/{teamId}/invites JavaScript example: ```javascript const { data: invites } = await cstar.members.invites.list(); ``` Response (200): ```json { "success": true, "data": [ { "id": "inv_abc123", "email": "new@team.com", "role": "agent", "status": "pending", "createdAt": "2025-12-10T14:30:00Z" } ] } ``` ##### POST /api/v1/teams/{teamId}/members/../invites Send a team invitation email. > Actual path: /api/v1/teams/{teamId}/invites Body parameters: - **email** (string) (required): Email to invite - **role** (string): Role to assign Values: admin, manager, librarian, agent, spectator JavaScript example: ```javascript await cstar.members.invites.create({ email: 'new@team.com', role: 'agent' }); ``` Response (201): ```json { "success": true, "data": { "id": "inv_abc123", "email": "new@team.com", "role": "agent", "status": "pending" } } ``` ##### GET /api/v1/teams/{teamId}/members/{memberId}/preferences Get an agent's per-user UX preferences. Currently exposes the boring-mode toggle that hides every gamification element for that agent. Requires `manage_members` permission. JavaScript example: ```javascript const prefs = await cstar.members.preferences.get(memberId); ``` Response (200): ```json { "success": true, "data": { "boringMode": false } } ``` ##### PATCH /api/v1/teams/{teamId}/members/{memberId}/preferences Toggle an agent's per-user UX preferences. Set `boringMode: true` to hide XP, bosses, and achievements for that agent (XP keeps tracking). Requires `manage_members` permission. Body parameters: - **boringMode** (boolean): When true, hides every gamification element for this agent. JavaScript example: ```javascript await cstar.members.preferences.update(memberId, { boringMode: true }); ``` Response (200): ```json { "success": true, "data": { "boringMode": true } } ``` --- ### Settings Base path: `/api/v1/teams/{teamId}/settings` Team settings control statuses, quick replies, business hours, and widget appearance. Secret key required. #### Settings object - **object** (string): Always "settings" - **teamName** (string): Team display name - **teamSlug** (string): URL-safe team identifier #### Endpoints ##### GET /api/v1/teams/{teamId}/settings Get all team settings. JavaScript example: ```javascript const { data: settings } = await cstar.settings.get(); ``` Response (200): ```json { "success": true, "data": { "object": "settings", "teamName": "Acme Support", "teamSlug": "acme" } } ``` ##### GET /api/v1/teams/{teamId}/settings/../preferences Get team-wide UX preferences. Currently exposes the boss-battles toggle that hides the team-wide gamification ladder. Requires `manage_settings` permission. > Actual path: /api/v1/teams/{teamId}/preferences JavaScript example: ```javascript const prefs = await cstar.preferences.get(); ``` Response (200): ```json { "success": true, "data": { "bossBattlesEnabled": true } } ``` ##### PATCH /api/v1/teams/{teamId}/settings/../preferences Toggle team-wide UX preferences. Set `bossBattlesEnabled: false` to hide the team boss feature for every agent. Requires `manage_settings` permission. > Actual path: /api/v1/teams/{teamId}/preferences Body parameters: - **bossBattlesEnabled** (boolean): When false, hides the team boss feature. JavaScript example: ```javascript await cstar.preferences.update({ bossBattlesEnabled: false }); ``` Response (200): ```json { "success": true, "data": { "bossBattlesEnabled": false } } ``` ##### PATCH /api/v1/teams/{teamId}/settings Update team settings. Only provided fields are changed. JavaScript example: ```javascript await cstar.settings.update({ teamName: 'New Name' }); ``` Response (200): ```json { "success": true, "data": { "object": "settings", "teamName": "New Name" } } ``` ##### GET /api/v1/teams/{teamId}/settings/statuses List all ticket status configurations. JavaScript example: ```javascript const { data: statuses } = await cstar.settings.statuses.list(); ``` Response (200): ```json { "success": true, "data": [ { "key": "open", "label": "Open", "color": "#3B82F6", "position": 1, "isProtected": true } ] } ``` ##### GET /api/v1/teams/{teamId}/settings/business-hours Get business hours configuration including timezone and weekly schedule. JavaScript example: ```javascript const { data: hours } = await cstar.settings.getBusinessHours(); ``` Response (200): ```json { "success": true, "data": { "object": "business_hours", "enabled": true, "timezone": "America/New_York", "schedule": { "monday": { "enabled": true, "start": "09:00", "end": "17:00" } } } } ``` ##### GET /api/v1/teams/{teamId}/settings/widget Get widget appearance and behavior settings. JavaScript example: ```javascript const { data: widget } = await cstar.settings.getWidget(); ``` Response (200): ```json { "success": true, "data": { "object": "widget_settings", "position": "bottom-right", "primaryColor": "#F87171" } } ``` --- ### Notifications Base path: `/api/v1/teams/{teamId}/notifications` In-app notifications for team members. List, read, and manage notification state. #### Notifications object - **id** (string): Notification ID - **type** (string): Notification type - **title** (string): Notification title - **body** (string): Notification body - **read** (boolean): Whether the notification has been read - **createdAt** (datetime): When the notification was created #### Endpoints ##### GET /api/v1/teams/{teamId}/notifications List notifications for the authenticated user. Query parameters: - **unreadOnly** (boolean): Only return unread notifications - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const { data } = await cstar.notifications.list({ unreadOnly: true }); ``` Response (200): ```json { "success": true, "data": [ { "id": "ntf_abc", "type": "ticket_assigned", "title": "Ticket assigned", "read": false } ] } ``` ##### GET /api/v1/teams/{teamId}/notifications/unread-count Get the number of unread notifications. JavaScript example: ```javascript const { data } = await cstar.notifications.unreadCount(); ``` Response (200): ```json { "success": true, "data": { "object": "unread_count", "count": 5 } } ``` ##### POST /api/v1/teams/{teamId}/notifications/mark-all-read Mark all notifications as read. JavaScript example: ```javascript await cstar.notifications.markAllRead(); ``` Response (200): ```json { "success": true, "data": { "updated": 5 } } ``` --- ### Custom Fields Base path: `/api/v1/teams/{teamId}/custom-fields` Define custom data fields for tickets and customers. Supports text, number, select, date, and boolean types. #### Custom Fields object - **id** (string): Field ID - **name** (string): Display name - **key** (string): Machine-readable key - **type** (string): Field type Values: text, number, select, multiselect, date, boolean - **entityType** (string): Which entity this field belongs to Values: tickets, customers - **required** (boolean): Whether the field is required - **options** (string[]): Options for select/multiselect fields - **position** (integer): Display order #### Endpoints ##### GET /api/v1/teams/{teamId}/custom-fields List all custom field definitions. Query parameters: - **entityType** (string): Filter by entity type Values: tickets, customers JavaScript example: ```javascript const { data } = await cstar.customFields.list({ entityType: 'tickets' }); ``` Response (200): ```json { "success": true, "data": [ { "id": "cf_abc", "name": "Priority Level", "key": "priority_level", "type": "select", "entityType": "tickets", "options": [ "P0", "P1", "P2" ] } ] } ``` ##### POST /api/v1/teams/{teamId}/custom-fields Create a new custom field definition. Body parameters: - **name** (string) (required): Display name - **key** (string) (required): Machine-readable key - **type** (string) (required): Field type Values: text, number, select, multiselect, date, boolean - **entityType** (string) (required): Entity type Values: tickets, customers - **options** (string[]): Options for select types JavaScript example: ```javascript await cstar.customFields.create({ name: 'Region', key: 'region', type: 'select', entityType: 'tickets', options: ['US', 'EU', 'APAC'] }); ``` Response (201): ```json { "success": true, "data": { "id": "cf_abc", "name": "Region", "key": "region", "type": "select" } } ``` --- ### SLA Rules Base path: `/api/v1/teams/{teamId}/sla/rules` Service Level Agreement rules define response and resolution time targets by ticket priority. #### SLA Rules object - **id** (string): Rule ID - **name** (string): Rule name - **priority** (string): Ticket priority this rule applies to - **responseTarget** (integer): Response time target in minutes - **resolutionTarget** (integer): Resolution time target in minutes - **businessHoursOnly** (boolean): Only count business hours - **enabled** (boolean): Whether the rule is active #### Endpoints ##### GET /api/v1/teams/{teamId}/sla/rules List all SLA rules. JavaScript example: ```javascript const { data } = await cstar.sla.list(); ``` Response (200): ```json { "success": true, "data": [ { "id": "sla_abc", "name": "Urgent SLA", "priority": "urgent", "responseTarget": 15, "resolutionTarget": 120, "enabled": true } ] } ``` ##### POST /api/v1/teams/{teamId}/sla/rules Create a new SLA rule. Body parameters: - **name** (string) (required): Rule name - **priority** (string) (required): Priority level - **responseTarget** (integer) (required): Response target in minutes - **resolutionTarget** (integer) (required): Resolution target in minutes - **businessHoursOnly** (boolean): Count only business hours JavaScript example: ```javascript await cstar.sla.create({ name: 'High Priority', priority: 'high', responseTarget: 30, resolutionTarget: 240 }); ``` Response (201): ```json { "success": true, "data": { "id": "sla_abc", "name": "High Priority", "priority": "high", "responseTarget": 30, "resolutionTarget": 240 } } ``` --- ### Analytics Base path: `/api/v1/teams/{teamId}/analytics` Team performance analytics, agent stats, and CSAT data. All analytics endpoints require a secret key. #### Analytics object - **period** (string): Time period for the data - **tickets.total** (integer): Total tickets in period - **tickets.open** (integer): Currently open tickets - **tickets.resolved** (integer): Tickets resolved in period - **customers.total** (integer): Total customers - **csat.average** (number): Average CSAT score #### Endpoints ##### GET /api/v1/teams/{teamId}/analytics/overview Get a summary of ticket, customer, and CSAT metrics for a time period. Query parameters: - **period** (string) (default: week): Time period Values: day, week, month, quarter, year JavaScript example: ```javascript const { data } = await cstar.analytics.overview({ period: 'week' }); ``` Response (200): ```json { "success": true, "data": { "object": "analytics_overview", "period": "week", "tickets": { "total": 142, "open": 23, "resolved": 89 }, "customers": { "total": 450, "active": 120, "new": 15 }, "csat": { "average": 4.2, "totalResponses": 67 } } } ``` ##### GET /api/v1/teams/{teamId}/analytics/agents Get per-agent performance metrics. JavaScript example: ```javascript const { data } = await cstar.analytics.agents(); ``` Response (200): ```json { "success": true, "data": [ { "object": "agent_stats", "userId": "usr_abc", "name": "Jane", "ticketsResolved": 45, "avgResponseTime": 12.5, "csatAverage": 4.8 } ] } ``` --- ### Bulk Operations Base path: `/api/v1/teams/{teamId}/bulk` Perform bulk updates or deletes on tickets, customers, or articles. Max 100 items per request. #### Bulk Operations object - **action** (string): Operation type Values: update, delete - **resource** (string): Resource type Values: tickets, customers, articles - **total** (integer): Total items processed - **succeeded** (integer): Items successfully processed - **failed** (integer): Items that failed #### Endpoints ##### POST /api/v1/teams/{teamId}/bulk Perform a bulk update or delete on up to 100 resources at once. Body parameters: - **action** (string) (required): Operation: update or delete Values: update, delete - **resource** (string) (required): Resource type Values: tickets, customers, articles - **ids** (string[]) (required): Array of resource IDs - **data** (object): Update fields (required for update action) JavaScript example: ```javascript await cstar.bulk.update({ resource: 'tickets', ids: ['tkt_1', 'tkt_2'], data: { status: 'closed' } }); ``` Response (200): ```json { "success": true, "data": { "object": "bulk_result", "action": "update", "resource": "tickets", "total": 5, "succeeded": 5, "failed": 0, "errors": [] } } ``` --- ### Export Base path: `/api/v1/teams/{teamId}/export` Export tickets, customers, or articles as JSON or CSV. Synchronous — max 10,000 rows per resource. #### Export object - **format** (string): Export format (json or csv) - **totalRows** (integer): Total rows exported #### Endpoints ##### POST /api/v1/teams/{teamId}/export Export data synchronously. Supports multi-resource JSON or single-resource CSV. Body parameters: - **resources** (array) (required): Array of { type, filters } objects - **format** (string): Output format Values: json, csv JavaScript example: ```javascript const { data } = await cstar.export.create({ resources: [{ type: 'tickets' }], format: 'json' }); ``` Response (200): ```json { "success": true, "data": { "object": "export", "format": "json", "totalRows": 142, "resources": { "tickets": [ { "id": "tkt_abc", "title": "Sample" } ] } } } ``` --- ### Import Base path: `/api/v1/teams/{teamId}/import` Import tickets, customers, or articles. Synchronous processing — max 500 records per request. Supports merge strategies. #### Import object - **resource** (string): Resource type imported - **total** (integer): Total records processed - **created** (integer): Records created - **updated** (integer): Records updated - **skipped** (integer): Records skipped #### Endpoints ##### POST /api/v1/teams/{teamId}/import Import records synchronously. Max 500 per request. Body parameters: - **resource** (string) (required): Resource type Values: tickets, customers, articles - **records** (array) (required): Array of record objects - **mergeStrategy** (string): How to handle duplicates Values: skip, overwrite, merge JavaScript example: ```javascript await cstar.import.create({ resource: 'customers', records: [...], mergeStrategy: 'merge' }); ``` Response (200): ```json { "success": true, "data": { "object": "import_result", "resource": "customers", "total": 50, "created": 45, "updated": 3, "skipped": 2, "errors": [] } } ``` ##### GET /api/v1/teams/{teamId}/import List import history logs. JavaScript example: ```javascript const { data } = await cstar.import.logs(); ``` Response (200): ```json { "success": true, "data": [ { "id": "imp_abc", "resource": "customers", "status": "completed", "totalRecords": 50, "created": 45, "createdAt": "2025-12-10T14:30:00Z" } ] } ``` --- ### Audit Log Base path: `/api/v1/teams/{teamId}/audit-log` Immutable audit trail of all actions taken in your team. Filter by action, resource, actor, or date range. #### Audit Log object - **id** (string): Log entry ID - **action** (string): Action performed (e.g., ticket.created) - **resourceType** (string): Type of resource affected - **resourceId** (string): ID of the affected resource - **actorName** (string): Name of who performed the action - **details** (object): Additional details about the action - **createdAt** (datetime): When the action occurred #### Endpoints ##### GET /api/v1/teams/{teamId}/audit-log List audit log entries with optional filtering. Query parameters: - **action** (string): Filter by action type - **resourceType** (string): Filter by resource type - **actorId** (string): Filter by actor - **startDate** (string): Start date (ISO 8601) - **endDate** (string): End date (ISO 8601) - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const { data } = await cstar.auditLog.list({ action: 'ticket.created' }); ``` Response (200): ```json { "success": true, "data": [ { "id": "aud_abc", "action": "ticket.created", "resourceType": "ticket", "resourceId": "tkt_abc", "actorName": "Jane Smith", "createdAt": "2025-12-10T14:30:00Z" } ] } ``` --- ### Search Base path: `/api/v1/teams/{teamId}/search` Search across tickets, customers, and articles in a single query. Works with publishable keys (read-only). #### Search object - **type** (string): Resource type (ticket, customer, article) - **id** (string): Resource ID - **title** (string): Title or name - **snippet** (string): Matching text excerpt #### Endpoints ##### GET /api/v1/teams/{teamId}/search Perform a cross-resource search. Query parameters: - **q** (string): Search query - **types** (string): Comma-separated types to search (tickets,customers,articles) - **limit** (integer) (default: 10): Max results JavaScript example: ```javascript const { data } = await cstar.search.query({ query: 'billing issue', types: ['tickets', 'customers'] }); ``` Response (200): ```json { "success": true, "data": [ { "object": "search_result", "type": "ticket", "id": "tkt_abc", "title": "Billing issue", "snippet": "...cannot process payment..." } ] } ``` --- ### Tags Base path: `/api/v1/teams/{teamId}/tags` Manage tags used across tickets. List tags with usage counts, create new ones, or remove unused tags. #### Tags object - **name** (string): Tag name - **count** (integer): Number of tickets using this tag #### Endpoints ##### GET /api/v1/teams/{teamId}/tags List all tags with usage counts. JavaScript example: ```javascript const { data } = await cstar.tags.list(); ``` Response (200): ```json { "success": true, "data": [ { "object": "tag", "name": "billing", "count": 23 }, { "object": "tag", "name": "login", "count": 15 } ] } ``` ##### POST /api/v1/teams/{teamId}/tags Create a new tag. Body parameters: - **name** (string) (required): Tag name JavaScript example: ```javascript await cstar.tags.create('new-tag'); ``` Response (201): ```json { "success": true, "data": { "object": "tag", "name": "new-tag", "count": 0 } } ``` --- ### Game Base path: `/api/v1/teams/{teamId}/game` The cStar gamification system — player stats, boss battles, leaderboards, achievements, quests, puzzles, social features, cosmetics, seasons, and skill trees. Read endpoints accept any API key; write endpoints require a secret key. #### Game object - **level** (integer): Player level (resets each season) - **xp** (integer): Current XP within the season - **gold** (integer): Gold balance (earned from tickets and bosses) - **skills** (object): Skill tree allocations (4 skills, max 5 each) #### Endpoints ##### GET /api/v1/teams/{teamId}/game/player Get a player's game stats including level, XP, gold, skills, streaks, and season info. Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.player({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "player_stats", "memberId": "usr_abc", "name": "Jane", "avatarUrl": null, "season": { "id": "2026-Q2", "name": "Season 3 — 2026", "endsAt": "2026-06-30T23:59:59Z" }, "level": 12, "xp": 4850, "xpToNextLevel": 1000, "xpInCurrentLevel": 850, "xpProgress": 0.85, "gold": 340, "prestigeLevel": 1, "prestigeBonus": 0.01, "streaks": { "current": 7, "max": 14 }, "stats": { "ticketsClosed": 156, "bossesDefeated": 8, "finalBlows": 2, "achievementsUnlocked": 24, "questsCompleted": 89 }, "skills": { "quick_learner": { "level": 3, "maxLevel": 5, "effect": "+15% XP" } }, "availableSkillPoints": 2, "totalSkillPoints": 8, "cosmetics": { "avatarFrame": "gold_ring", "victoryMessage": "GG!" } } } ``` ##### GET /api/v1/teams/{teamId}/game/player/{memberId} Get game stats for a specific team member by their membership ID. Path parameters: - **memberId** (string): Membership ID of the player JavaScript example: ```javascript const { data } = await cstar.game.player('mem_abc'); ``` Response (200): ```json { "success": true, "data": { "object": "player_stats" } } ``` ##### GET /api/v1/teams/{teamId}/game/boss Get the current boss battle state including health, phase, abilities, participants, and battle duration. JavaScript example: ```javascript const { data } = await cstar.game.boss.current(); ``` Response (200): ```json { "success": true, "data": { "object": "boss_state", "active": true, "boss": { "id": "boss_abc", "name": "The Backlog Beast", "level": 47, "difficulty": "hard", "maxHealth": 3500, "currentHealth": 2180, "healthPercent": 0.623, "phase": 2, "abilities": { "shield": null, "enrage": { "active": true }, "heal": { "totalHealed": 175 } }, "rewards": { "xpMultiplier": 2.5, "goldMultiplier": 2.5 }, "participants": [ { "memberId": "usr_abc", "name": "Jane", "damage": 780, "hits": 12, "lastHitAt": "2026-04-06T12:00:00Z" } ], "totalDamage": 1320, "spawnedAt": "2026-04-06T10:00:00Z", "battleDuration": 7200 } } } ``` ##### GET /api/v1/teams/{teamId}/game/boss/active Lightweight check if a boss battle is currently active. JavaScript example: ```javascript const { data } = await cstar.game.boss.isActive(); ``` Response (200): ```json { "success": true, "data": { "object": "boss_active", "active": true } } ``` ##### POST /api/v1/teams/{teamId}/game/boss/damage Deal damage to the current boss. Damage is calculated server-side based on ticket priority, skills, and combo state. Clients cannot specify damage values. Body parameters: - **userId** (string) (required): User dealing damage - **userName** (string): Display name for leaderboard - **priority** (string): Ticket priority (affects damage) Values: low, normal, high, urgent JavaScript example: ```javascript const { data } = await cstar.game.boss.damage({ userId: 'usr_abc', priority: 'high' }); ``` Response (200): ```json { "success": true, "data": { "object": "boss_damage", "damage": 127, "currentHealth": 2053, "healthPercent": 0.587, "previousHealthPercent": 0.623, "isDefeated": false, "isKillingBlow": false, "shieldBlocked": false, "shieldBroken": false, "enrageTriggered": false, "activeAbilities": [ "enrage" ] } } ``` ##### POST /api/v1/teams/{teamId}/game/boss/spawn Spawn a new boss battle. Fails if a boss is already active. Body parameters: - **difficulty** (string): Boss difficulty Values: easy, medium, hard JavaScript example: ```javascript await cstar.game.boss.spawn({ difficulty: 'hard' }); ``` Response (201): ```json { "success": true, "data": { "object": "boss_state", "active": true, "boss": { "name": "Queue Crusher", "difficulty": "medium", "maxHealth": 1500, "currentHealth": 1500, "healthPercent": 1 } } } ``` ##### GET /api/v1/teams/{teamId}/game/boss/defeats Get the history of defeated bosses with participants and rewards. Query parameters: - **page** (integer) (default: 1): Page number - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const { data } = await cstar.game.boss.defeats({ pageSize: 5 }); ``` Response (200): ```json { "success": true, "data": { "object": "list", "data": [ { "id": "def_abc", "bossName": "The Backlog Beast", "bossLevel": 47, "difficulty": "hard", "totalDamage": 3500, "finalBlow": { "memberId": "usr_abc", "name": "Jane", "damage": 89 }, "defeatedAt": "2026-04-05T18:00:00Z" } ] } } ``` ##### GET /api/v1/teams/{teamId}/game/boss/defeats/{defeatId} Get full details for a specific boss defeat. Path parameters: - **defeatId** (string): Boss defeat record ID JavaScript example: ```javascript const { data } = await cstar.game.boss.defeats.get('def_abc'); ``` Response (200): ```json { "success": true, "data": { "object": "boss_defeat", "bossName": "The Backlog Beast", "participants": [] } } ``` ##### GET /api/v1/teams/{teamId}/game/leaderboard Get team leaderboard rankings for a given period. Query parameters: - **period** (string) (default: weekly): Leaderboard period Values: daily, weekly, allTime - **limit** (integer) (default: 20): Max results (max 50) JavaScript example: ```javascript const { data } = await cstar.game.leaderboard({ period: 'weekly' }); ``` Response (200): ```json { "success": true, "data": { "object": "leaderboard", "period": "weekly", "entries": [ { "rank": 1, "memberId": "usr_abc", "name": "Jane", "score": 2450, "ticketsClosed": 34, "bossDamage": 890, "currentStreak": 12 } ], "totalParticipants": 8, "periodStartedAt": "2026-04-01T00:00:00Z", "periodEndsAt": "2026-04-08T00:00:00Z" } } ``` ##### GET /api/v1/teams/{teamId}/game/leaderboard/me Get the current user's rank on the leaderboard. Query parameters: - **userId** (string): User ID - **period** (string) (default: weekly): Period Values: daily, weekly, allTime JavaScript example: ```javascript const { data } = await cstar.game.leaderboard.me({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "leaderboard_entry", "rank": 3, "totalParticipants": 8, "score": 1890 } } ``` ##### GET /api/v1/teams/{teamId}/game/achievements List all achievements with rarity, progress, and summary stats. Query parameters: - **userId** (string): User ID - **category** (string): Filter by category (volume, boss, streak, etc.) - **unlockedOnly** (boolean): Only show unlocked achievements JavaScript example: ```javascript const { data } = await cstar.game.achievements({ userId: 'usr_abc', category: 'boss' }); ``` Response (200): ```json { "success": true, "data": { "object": "achievement_list", "data": [ { "id": "first_blood", "name": "First Blood", "description": "Close your first ticket", "category": "volume", "rarity": "common", "xpReward": 25, "hidden": false, "unlocked": true, "progress": { "current": 156, "target": 1 } } ], "summary": { "total": 100, "unlocked": 24, "totalXpEarned": 3200, "totalGoldEarned": 1600, "byCategory": { "volume": { "total": 11, "unlocked": 5 } }, "byRarity": { "common": { "total": 30, "unlocked": 18 } } } } } ``` ##### GET /api/v1/teams/{teamId}/game/achievements/{achievementId} Get details and progress for a specific achievement. Path parameters: - **achievementId** (string): Achievement identifier Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.achievements.get('centurion', { userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "id": "centurion", "object": "achievement", "name": "Centurion", "unlocked": true, "progress": { "current": 156, "target": 100 } } } ``` ##### GET /api/v1/teams/{teamId}/game/achievements/recent Get recently unlocked achievements across the team. Query parameters: - **limit** (integer) (default: 10): Max results (max 50) JavaScript example: ```javascript const { data } = await cstar.game.achievements.recent(); ``` Response (200): ```json { "success": true, "data": { "object": "list", "data": [ { "id": "centurion", "name": "Centurion", "rarity": "rare", "unlockedBy": { "memberId": "usr_abc", "name": "Jane" }, "unlockedAt": "2026-04-06T12:00:00Z" } ] } } ``` ##### GET /api/v1/teams/{teamId}/game/quests/today Get today's daily quests with progress. Quests reset at midnight UTC. Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.quests.today({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "quest_list", "quests": [ { "questId": "close_5_tickets", "name": "Ticket Tackler", "description": "Close 5 tickets today", "difficulty": "easy", "progress": 3, "target": 5, "completed": false, "xpReward": 50 } ], "completedToday": 1, "totalQuests": 4, "allComplete": false, "completionBonus": null, "resetsAt": "2026-04-07T00:00:00Z" } } ``` ##### GET /api/v1/teams/{teamId}/game/quests/history Get past quest completions grouped by date. Query parameters: - **userId** (string): User ID - **limit** (integer) (default: 30): Max days (max 90) JavaScript example: ```javascript const { data } = await cstar.game.quests.history({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "quest_history", "history": [ { "date": "2026-04-05", "quests": [], "completedCount": 3 } ] } } ``` ##### GET /api/v1/teams/{teamId}/game/skills Get the skill tree with current allocations and available points. 4 skills: Quick Learner (+XP), Treasure Hunter (+Gold), Boss Slayer (+Damage), Social Butterfly (+1-Ups). Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.skills({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "skill_tree", "seasonId": "2026-Q2", "skills": [ { "id": "quick_learner", "name": "Quick Learner", "description": "+5% XP per level", "maxLevel": 5, "bonusPerLevel": 0.05, "currentLevel": 3, "isMaxed": false } ], "totalAllocated": 6, "availablePoints": 2, "playerLevel": 12 } } ``` ##### POST /api/v1/teams/{teamId}/game/skills/allocate Spend an available skill point. Earn 1 point every 5 levels. Body parameters: - **userId** (string) (required): User ID - **skillId** (string) (required): Skill ID Values: quick_learner, treasure_hunter, boss_slayer, social_butterfly JavaScript example: ```javascript await cstar.game.skills.allocate({ userId: 'usr_abc', skillId: 'quick_learner' }); ``` Response (200): ```json { "success": true, "data": { "object": "skill_allocation", "skillId": "quick_learner", "newLevel": 4, "availablePoints": 1 } } ``` ##### POST /api/v1/teams/{teamId}/game/skills/reset Reset all skill allocations, returning points to the available pool. Body parameters: - **userId** (string) (required): User ID JavaScript example: ```javascript await cstar.game.skills.reset({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "skill_reset", "skills": { "quick_learner": 0, "treasure_hunter": 0, "boss_slayer": 0, "social_butterfly": 0 }, "availablePoints": 8 } } ``` ##### GET /api/v1/teams/{teamId}/game/seasons/current Get the current active season with badge thresholds and days remaining. JavaScript example: ```javascript const { data } = await cstar.game.seasons.current(); ``` Response (200): ```json { "success": true, "data": { "object": "season", "id": "2026-Q2", "name": "Season 3 — 2026", "isActive": true, "startsAt": "2026-04-01T00:00:00Z", "endsAt": "2026-06-30T23:59:59Z", "daysRemaining": 85, "badges": [ { "level": 10, "name": "Participant", "color": "#8B4513" }, { "level": 25, "name": "Bronze", "color": "#CD7F32" }, { "level": 50, "name": "Silver", "color": "#C0C0C0" }, { "level": 75, "name": "Gold", "color": "#FFD700" }, { "level": 100, "name": "Diamond", "color": "#B9F2FF" } ], "teamParticipants": 8 } } ``` ##### GET /api/v1/teams/{teamId}/game/seasons Get all seasons (current and past). Query parameters: - **limit** (integer) (default: 10): Max results (max 20) JavaScript example: ```javascript const { data } = await cstar.game.seasons.list(); ``` Response (200): ```json { "success": true, "data": [ { "object": "season", "id": "2026-Q1", "isActive": false, "daysRemaining": 0 } ] } ``` ##### GET /api/v1/teams/{teamId}/game/seasons/{seasonId}/stats Get a user's stats for a specific season (current or past). Path parameters: - **seasonId** (string): Season identifier (e.g. 2026-Q1) Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.seasons.stats('2026-Q1', { userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "season_stats", "seasonId": "2026-Q1", "level": 18, "xp": 18000, "ticketsClosed": 342 } } ``` ##### GET /api/v1/teams/{teamId}/game/one-ups Get recent one-ups on the team. Query parameters: - **limit** (integer) (default: 20): Max results (max 50) - **userId** (string): Filter by sender or recipient JavaScript example: ```javascript const { data } = await cstar.game.oneUps.list(); ``` Response (200): ```json { "success": true, "data": { "object": "list", "data": [ { "id": "1up_abc", "object": "one_up", "sender": { "id": "usr_abc", "name": "Jane" }, "recipient": { "id": "usr_bob", "name": "Bob" }, "message": "Great work!", "goldAmount": 0, "wasFree": true, "createdAt": "2026-04-06T12:00:00Z" } ] } } ``` ##### POST /api/v1/teams/{teamId}/game/one-ups Send a one-up to a teammate. 3 free per day (+ social_butterfly skill bonus). Gold one-ups deduct from sender balance. Max 140 char message, max 100 gold. Body parameters: - **senderId** (string) (required): Sender user ID - **senderName** (string): Sender display name - **recipientId** (string) (required): Recipient user ID - **recipientName** (string): Recipient display name - **message** (string): Kudos message (max 140 chars) - **goldAmount** (integer): Gold to gift (0-100) JavaScript example: ```javascript await cstar.game.oneUps.send({ senderId: 'usr_abc', recipientId: 'usr_bob', message: 'Great work!' }); ``` Response (201): ```json { "success": true, "data": { "object": "one_up", "id": "1up_abc", "wasFree": true, "freeRemaining": 2, "sender": { "id": "usr_abc", "name": "Jane" }, "recipient": { "id": "usr_bob", "name": "Bob" }, "message": "Great work!", "goldAmount": 0, "recipientXpAwarded": 10, "recipientGoldAwarded": 5 } } ``` ##### GET /api/v1/teams/{teamId}/game/one-ups/remaining Check how many free one-ups remain today. Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.oneUps.remaining({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "one_up_remaining", "freeOneUpsRemaining": 2, "maxFreeOneUps": 4, "sentToday": 1 } } ``` ##### GET /api/v1/teams/{teamId}/game/high-fives Get recent high fives sent to or from this team. Query parameters: - **limit** (integer) (default: 20): Max results (max 50) JavaScript example: ```javascript const { data } = await cstar.game.highFives.list(); ``` Response (200): ```json { "success": true, "data": { "object": "list", "data": [ { "id": "hf_abc", "object": "high_five", "senderTeam": { "id": "team_abc", "name": "Support Heroes" }, "recipientTeam": { "id": "team_xyz", "name": "Bug Busters" }, "message": "Crushing it!", "createdAt": "2026-04-06T12:00:00Z" } ] } } ``` ##### POST /api/v1/teams/{teamId}/game/high-fives Send a high five to another team. Limited to 1 per team per day. Max 80 char message. Body parameters: - **recipientTeamId** (string) (required): Target team ID - **recipientTeamName** (string): Target team name - **senderUserId** (string): Sender user ID - **senderUserName** (string): Sender name - **senderTeamName** (string): Your team name - **message** (string): Message (max 80 chars) JavaScript example: ```javascript await cstar.game.highFives.send({ recipientTeamId: 'team_xyz', message: 'Amazing work!' }); ``` Response (201): ```json { "success": true, "data": { "id": "hf_abc", "object": "high_five", "senderTeam": { "id": "team_abc", "name": "Support Heroes" }, "recipientTeam": { "id": "team_xyz", "name": "Bug Busters" } } } ``` ##### GET /api/v1/teams/{teamId}/game/high-fives/can-send/{recipientTeamId} Check if you can send a high five to a specific team today. Path parameters: - **recipientTeamId** (string): Team ID of the recipient JavaScript example: ```javascript const { data } = await cstar.game.highFives.canSend('team_xyz'); ``` Response (200): ```json { "success": true, "data": { "canSend": true, "highFivesRemaining": 1 } } ``` ##### GET /api/v1/teams/{teamId}/game/cosmetics Get all cosmetic items with unlock status. Query parameters: - **userId** (string): User ID - **type** (string): Filter by type Values: frame, theme, season_badge JavaScript example: ```javascript const { data } = await cstar.game.cosmetics({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": [ { "id": "gold_ring", "type": "frame", "name": "Gold Ring", "unlocked": true, "equipped": true } ] } ``` ##### GET /api/v1/teams/{teamId}/game/cosmetics/preferences Get the user's currently equipped cosmetics. Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.cosmetics.preferences({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "cosmetic_preferences", "avatarFrame": "gold_ring", "victoryMessage": "GG!" } } ``` ##### PATCH /api/v1/teams/{teamId}/game/cosmetics/preferences Equip a cosmetic. Can only equip unlocked items. Victory message max 200 chars. Body parameters: - **userId** (string) (required): User ID - **avatarFrame** (string): Frame ID to equip - **victoryMessage** (string): Victory message (max 200 chars) JavaScript example: ```javascript await cstar.game.cosmetics.equip({ userId: 'usr_abc', avatarFrame: 'fire_frame' }); ``` Response (200): ```json { "success": true, "data": { "object": "cosmetic_preferences", "avatarFrame": "fire_frame", "victoryMessage": "GG!" } } ``` ##### GET /api/v1/teams/{teamId}/game/puzzles/today Get today's daily warm-up puzzle. Query parameters: - **userId** (string): Include completion status if provided JavaScript example: ```javascript const { data } = await cstar.game.puzzles.today(); ``` Response (200): ```json { "success": true, "data": { "object": "daily_puzzle", "available": true, "puzzle": { "id": "puz_abc", "date": "2026-04-06", "type": "categories", "data": {} }, "completion": null } } ``` ##### POST /api/v1/teams/{teamId}/game/puzzles/complete Record a puzzle completion. Awards XP based on time and mistakes. Body parameters: - **userId** (string) (required): User ID - **puzzleId** (string) (required): Puzzle ID - **timeSeconds** (integer) (required): Time to complete - **mistakes** (integer) (required): Number of mistakes (0-4) - **isWon** (boolean) (required): Whether the puzzle was solved JavaScript example: ```javascript await cstar.game.puzzles.complete({ userId: 'usr_abc', puzzleId: 'puz_abc', timeSeconds: 42, mistakes: 1, isWon: true }); ``` Response (201): ```json { "success": true, "data": { "object": "puzzle_completion", "score": 850, "xpEarned": 25, "streak": { "current": 6, "longest": 12, "totalCompleted": 45 } } } ``` ##### GET /api/v1/teams/{teamId}/game/puzzles/leaderboard Get puzzle leaderboard — by scores (for a specific puzzle) or by streaks. Query parameters: - **type** (string) (default: streaks): Leaderboard type Values: scores, streaks - **puzzleId** (string): Required when type=scores - **limit** (integer) (default: 10): Max results (max 50) JavaScript example: ```javascript const { data } = await cstar.game.puzzles.leaderboard({ type: 'streaks' }); ``` Response (200): ```json { "success": true, "data": { "object": "puzzle_leaderboard", "type": "streaks", "entries": [ { "rank": 1, "userId": "usr_abc", "currentStreak": 12, "longestStreak": 20 } ] } } ``` ##### GET /api/v1/teams/{teamId}/game/puzzles/streak Get a user's puzzle streak stats. Query parameters: - **userId** (string): User ID JavaScript example: ```javascript const { data } = await cstar.game.puzzles.streak({ userId: 'usr_abc' }); ``` Response (200): ```json { "success": true, "data": { "object": "puzzle_streak", "currentStreak": 5, "longestStreak": 12, "totalCompleted": 45 } } ``` ##### GET /api/v1/teams/{teamId}/game/activity Get the team-wide game activity feed. Supports cursor-based pagination. Query parameters: - **limit** (integer) (default: 50): Max results (max 100) - **before** (string): Cursor — activity ID to paginate from JavaScript example: ```javascript const { data } = await cstar.game.activity({ limit: 20 }); ``` Response (200): ```json { "success": true, "data": { "object": "list", "data": [ { "id": "act_abc", "action": "boss_defeated", "actor": { "id": "usr_abc", "name": "Jane" }, "icon": "skull", "metadata": { "bossName": "The Backlog Beast" }, "createdAt": "2026-04-06T12:00:00Z" } ], "cursor": "act_abc" } } ``` ##### GET /api/v1/teams/{teamId}/game/activity/member/{memberId} Get activity feed for a specific team member. Path parameters: - **memberId** (string): Membership ID of the team member Query parameters: - **limit** (integer) (default: 20): Max results (max 100) JavaScript example: ```javascript const { data } = await cstar.game.activity.member('usr_abc'); ``` Response (200): ```json { "success": true, "data": { "object": "list", "data": [] } } ``` ##### GET /api/v1/teams/{teamId}/game/config Get game balance constants (XP per level, boss difficulties, reward tables, etc). Read-only, no database calls. Any API key can access. JavaScript example: ```javascript const { data } = await cstar.game.config(); ``` Response (200): ```json { "success": true, "data": { "object": "game_config", "rewards": { "ticketRewards": { "low": { "xp": 10, "gold": 5 }, "normal": { "xp": 25, "gold": 10 }, "high": { "xp": 50, "gold": 20 }, "urgent": { "xp": 75, "gold": 30 } } }, "leveling": { "xpPerLevel": 1000, "prestigeThreshold": 25, "prestigeBonus": 0.01 }, "social": { "freeOneUpsPerDay": 3, "maxGoldGift": 100, "highFivesPerTeamPerDay": 1 }, "scoring": { "phi": 1.618, "priorityMultipliers": { "urgent": 4.236, "high": 2.618, "normal": 1.618, "low": 1 } } } } ``` --- ### AI Actions Base path: `/api/v1/teams/{teamId}/ai` AI-powered actions for ticket management — auto-tag tickets, generate reply suggestions, summarize conversations, and improve draft text. #### AI Actions object - **action** (string): AI action type Values: auto-tag, suggest-reply, summarize, improve - **result** (object): Action-specific result data #### Endpoints ##### POST /api/v1/teams/{teamId}/ai Run an AI action. Available actions: auto-tag (tag a ticket), suggest-reply (generate a response), summarize (summarize a conversation), improve (refine draft text). Body parameters: - **action** (string) (required): Action type Values: auto-tag, suggest-reply, summarize, improve - **ticketId** (string): Ticket ID (for auto-tag, suggest-reply, summarize) - **text** (string): Text to improve (for improve action) JavaScript example: ```javascript // Suggest a reply const { data } = await cstar.ai.suggestReply('tkt_abc123'); // Auto-tag a ticket await cstar.ai.autoTag('tkt_abc123'); // Summarize a conversation await cstar.ai.summarize('tkt_abc123'); // Improve draft text await cstar.ai.improve('here is my respons to the cusomer'); ``` Response (200): ```json { "success": true, "data": { "object": "ai_result", "action": "suggest-reply", "result": { "suggestion": "Hi Jane, I'd be happy to help with your billing issue. Let me look into that for you right away." } } } ``` --- ### Automations Base path: `/api/v1/teams/{teamId}/automations` Automation rules that trigger actions when specific events occur. Rules have conditions, actions, and can be tested with dry runs. #### Automations object - **id** (string): Rule ID - **name** (string): Rule name - **triggerEvent** (string): Event that triggers this rule - **conditions** (object): Conditions that must match (all/any) - **actions** (array): Actions to execute when triggered - **enabled** (boolean): Whether the rule is active - **executionCount** (integer): Total times this rule has fired #### Endpoints ##### GET /api/v1/teams/{teamId}/automations List all automation rules. JavaScript example: ```javascript const { data } = await cstar.automations.list(); ``` Response (200): ```json { "success": true, "data": [ { "id": "rule_abc", "name": "Auto-assign urgent", "triggerEvent": "ticket.created", "enabled": true, "executionCount": 234 } ] } ``` ##### POST /api/v1/teams/{teamId}/automations Create a new automation rule. Body parameters: - **name** (string) (required): Rule name - **triggerEvent** (string) (required): Trigger event - **conditions** (object): Match conditions - **actions** (array): Actions to execute - **enabled** (boolean): Enable immediately JavaScript example: ```javascript await cstar.automations.create({ name: 'Auto-assign urgent', triggerEvent: 'ticket.created', conditions: { all: [{ field: 'priority', operator: 'equals', value: 'urgent' }] }, actions: [{ type: 'assign', agentId: 'usr_abc' }] }); ``` Response (201): ```json { "success": true, "data": { "id": "rule_abc", "name": "Auto-assign urgent", "triggerEvent": "ticket.created", "enabled": true } } ``` ##### POST /api/v1/teams/{teamId}/automations/{ruleId}/test Dry-run a rule against a specific resource. No actions are executed — just shows what would happen. Path parameters: - **ruleId** (string): Rule ID Body parameters: - **resourceType** (string) (required): Resource type to test against - **resourceId** (string) (required): Resource ID to test against JavaScript example: ```javascript await cstar.automations.test('rule_abc', { resourceType: 'ticket', resourceId: 'tkt_abc' }); ``` Response (200): ```json { "success": true, "data": { "wouldMatch": true, "conditionResults": { "all": [ { "field": "priority", "passed": true } ] }, "actionsWouldExecute": [ { "action": "assign", "success": true } ] } } ``` --- ### Saved Views Base path: `/api/v1/teams/{teamId}/views` Saved filter presets for tickets, customers, articles, or community posts. Create reusable views and execute them to get filtered results. #### Saved Views object - **id** (string): View ID - **name** (string): View name - **entityType** (string): Entity type Values: tickets, customers, articles, community_posts - **filters** (object): Saved filter configuration - **sortField** (string): Sort field - **sortDirection** (string): Sort direction Values: asc, desc - **visibility** (string): Who can see this view Values: private, team #### Endpoints ##### GET /api/v1/teams/{teamId}/views List saved views, optionally filtered by entity type. Query parameters: - **entityType** (string): Filter by entity type Values: tickets, customers, articles, community_posts JavaScript example: ```javascript const { data } = await cstar.views.list({ entityType: 'tickets' }); ``` Response (200): ```json { "success": true, "data": [ { "id": "view_abc", "name": "Urgent Unresolved", "entityType": "tickets", "filters": { "priority": "urgent", "status": "open" }, "visibility": "team" } ] } ``` ##### POST /api/v1/teams/{teamId}/views Create a saved view. Body parameters: - **name** (string) (required): View name - **entityType** (string) (required): Entity type Values: tickets, customers, articles, community_posts - **filters** (object): Filter configuration - **sortField** (string): Sort field - **sortDirection** (string): Sort direction Values: asc, desc - **visibility** (string): Visibility Values: private, team JavaScript example: ```javascript await cstar.views.create({ name: 'Urgent Unresolved', entityType: 'tickets', filters: { priority: 'urgent', status: 'open' } }); ``` Response (201): ```json { "success": true, "data": { "id": "view_abc", "name": "Urgent Unresolved", "entityType": "tickets", "filters": { "priority": "urgent" } } } ``` ##### GET /api/v1/teams/{teamId}/views/{viewId}/tickets Execute a saved view — returns the filtered and sorted results. Path parameters: - **viewId** (string): View ID Query parameters: - **page** (integer) (default: 1): Page number (1-indexed) - **pageSize** (integer) (default: 20): Results per page (max 100) JavaScript example: ```javascript const { data } = await cstar.views.execute('view_abc'); ``` Response (200): ```json { "success": true, "data": [ { "id": "tkt_abc", "title": "Urgent billing issue", "priority": "urgent", "status": "open" } ], "meta": { "pagination": { "page": 1, "pageSize": 20, "total": 142, "totalPages": 8 } } } ``` --- ### Billing Base path: `/api/v1/teams/{teamId}/billing` Access subscription status and create Stripe billing portal sessions. Secret key required. #### Billing object - **status** (string): Subscription status - **plan** (string): Current plan - **memberCount** (integer): Number of team members - **currentPeriodEnd** (datetime): When the current billing period ends #### Endpoints ##### GET /api/v1/teams/{teamId}/billing/subscription Get the current subscription status and plan details. JavaScript example: ```javascript const { data } = await cstar.billing.subscription(); ``` Response (200): ```json { "success": true, "data": { "object": "subscription", "status": "active", "plan": "team", "memberCount": 5, "currentPeriodEnd": "2026-01-15T00:00:00Z", "cancelAtPeriodEnd": false } } ``` ##### POST /api/v1/teams/{teamId}/billing/portal Create a Stripe billing portal session. Returns a URL where the team admin can manage their subscription. Body parameters: - **returnUrl** (string): URL to redirect back to after portal session JavaScript example: ```javascript const { data } = await cstar.billing.portal({ returnUrl: 'https://yourapp.com/settings' }); ``` Response (200): ```json { "success": true, "data": { "object": "billing_portal", "url": "https://billing.stripe.com/session/..." } } ``` --- ## Webhooks Events follow the `resource.action` pattern. Subscribe to specific events when creating webhooks via the API or CLI (`cstar listen`). #### Tickets - **ticket.created**: Fired when a new ticket is created via app, widget, API, or email - **ticket.updated**: Fired when a ticket status, priority, or assignment changes - **ticket.closed**: Fired when a ticket is resolved or closed - **ticket.message_added**: Fired when a new message is added to a ticket - **ticket.deleted**: Fired when a ticket is deleted via app or API - **ticket.assigned**: Fired when a ticket is assigned to an agent #### Customers - **customer.created**: Fired when a new customer is created - **customer.updated**: Fired when customer details are modified - **customer.deleted**: Fired when a customer is deleted via app or API - **customer_group.created**: Fired when a customer group is created - **customer_group.updated**: Fired when a customer group is modified - **customer_group.deleted**: Fired when a customer group is deleted #### Articles - **article.created**: Fired when a new Library article is created - **article.published**: Fired when an article is published to the Public Library - **article.updated**: Fired when an article content or metadata is modified - **article.deleted**: Fired when an article is deleted via app or API #### Team - **team.preferences_updated**: Fired when team-wide UX preferences change (e.g., bossBattlesEnabled toggled) - **agent.preferences_updated**: Fired when an agent's per-user UX preferences change (e.g., boringMode toggled) #### Gamification - **boss.spawned**: Fired when a boss appears for the team - **boss.damaged**: Fired when a boss takes damage from a ticket close - **boss.defeated**: Fired when a boss is defeated by the team - **player.level_up**: Fired when a player reaches a new level - **player.achievement_unlocked**: Fired when a player unlocks an achievement - **player.xp_gained**: Fired when a player earns XP from closing a ticket or quest - **player.quest_completed**: Fired when a player completes a daily quest - **player.streak_milestone**: Fired when a player hits a streak milestone (3, 5, 10, or 25 days) - **social.one_up_sent**: Fired when a teammate sends a one-up recognition - **social.high_five_sent**: Fired when a team sends a high five to another team #### Community - **community_post.created**: Fired when a new community post is created - **community_post.updated**: Fired when a community post is updated - **community_post.status_changed**: Fired when a community post status is changed (e.g., Planned, Completed) - **community_post.commented**: Fired when a new comment is added to a community post - **community_post.deleted**: Fired when a community post is deleted - **community_post.voted**: Fired when a community post receives a vote #### Csat - **survey.submitted**: Fired when a customer submits a CSAT survey response #### Payload format ```json { "id": "evt_abc123", "type": "ticket.created", "created_at": "2026-04-27T18:42:11.000Z", "team_id": "your-team-id", "data": { "ticket": { "id": "tkt_...", "title": "...", "status": "new" }, "customer": { "id": "cus_...", "name": "...", "email": "..." } } } ``` #### Headers sent with each delivery - `X-Signature`: Stripe-style signature `t=,v1=`. Legacy `sha256=` is still accepted by the verifier for back-compat but no longer emitted. - `X-Event-Type`: Event type (e.g., `ticket.created`). - `X-Event-ID`: Unique event ID — use as your idempotency key. - `X-Webhook-ID`: Your webhook subscription ID. - `X-Timestamp`: ISO 8601 delivery timestamp. - `X-Delivery-Attempt`: Attempt number (starts at 1). - `x-cstar-request-id`: Correlates the webhook back to the API call that triggered it. #### Signature verification ```javascript import { createHmac, timingSafeEqual } from 'node:crypto'; /** * Verify a Stripe-style webhook signature. * @param {string} body - Raw request body (string, not parsed JSON). * @param {string} header - Value of the X-Signature header. * @param {string} secret - Your webhook signing secret. * @param {number} toleranceSeconds - Replay window (default 300s = 5 min). */ function verifyWebhook(body, header, secret, toleranceSeconds = 300) { const parts = Object.fromEntries( header.split(',').map((p) => p.split('=')) ); const ts = Number(parts.t); const sig = parts.v1; if (!ts || !sig) return false; if (Math.abs(Date.now() / 1000 - ts) > toleranceSeconds) return false; const expected = createHmac('sha256', secret) .update(`${ts}.${body}`) .digest('hex'); const a = Buffer.from(sig, 'hex'); const b = Buffer.from(expected, 'hex'); return a.length === b.length && timingSafeEqual(a, b); } ``` Or use the helper exported by `@cstar.help/js/webhook`: ```javascript import { verifySignature } from '@cstar.help/js/webhook'; if (!verifySignature(rawBody, req.headers['x-signature'], process.env.CSTAR_WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } ``` #### Retry policy Failed deliveries (non-2xx response or timeout) retry with exponential backoff. The `X-Delivery-Attempt` header tells you which attempt this is. --- ## SDKs ### JavaScript — `@cstar.help/js` v0.15.1 Install: `npm install @cstar.help/js` ```javascript import { CStarClient } from '@cstar.help/js'; const cstar = new CStarClient({ apiKey: process.env.CSTAR_SECRET_KEY, // sk_live_* or sk_test_* teamId: process.env.CSTAR_TEAM_ID }); // CRUD const { data: tickets } = await cstar.tickets.list({ status: 'open' }); const { data: ticket } = await cstar.tickets.create({ title: 'Need help', priority: 'high' }); await cstar.tickets.update(ticket.id, { status: 'resolved' }); await cstar.tickets.delete(ticket.id); // Last response metadata (request ID, timestamp, pagination) console.log(cstar.lastMeta.requestId); // Real-time event stream (SSE) const off = cstar.realtime.on('ticket.*', (event) => { console.log(event.type, event.data); }); // off() to unsubscribe; cstar.destroy() to tear everything down ``` Available resource accessors on the client: `tickets`, `customers`, `articles`, `webhooks`, `automations`, `categories`, `community.posts`, `community.topics`, `members`, `settings`, `notifications`, `mentions`, `attachments`, `customFields`, `sla`, `analytics`, `bulk`, `export`, `import`, `auditLog`, `search`, `tags`, `activity`, `customerGroups`, `game`, `ai`, `views`, `billing`. Plus `realtime` and (when `offline: true`) `offlineQueue`. ### JS — Customer auth (`@cstar.help/js/auth`) ```javascript import { AuthClient } from '@cstar.help/js/auth'; const auth = new AuthClient({ teamSlug: 'acme' }); await auth.signup({ email: 'jane@example.com', password: 'securepass', name: 'Jane' }); await auth.login({ email: 'jane@example.com', password: 'securepass' }); auth.isAuthenticated; // true auth.accessToken; // JWT auth.getCustomer(); // { id, email, name } auth.logout(); ``` ### JS — Public knowledge base (`@cstar.help/js/library`) ```javascript import { LibraryClient } from '@cstar.help/js/library'; const library = new LibraryClient({ teamSlug: 'acme' }); const categories = await library.categories(); const articles = await library.articles({ categorySlug: 'billing' }); const article = await library.article('reset-password'); const results = await library.search('reset password'); const popular = await library.popularArticles(5); const stats = await library.stats(); ``` ### React — `@cstar.help/react` v0.10.0 Install: `npm install @cstar.help/react` ```jsx import { CStarProvider, useTickets, useCustomers, useArticles, useCStarClient, useSubscription } from '@cstar.help/react'; function App() { return ( ); } function TicketList() { const { tickets, loading, error, refetch, pagination } = useTickets({ status: 'open' }); const client = useCStarClient(); useSubscription('ticket.created', (event) => refetch()); if (loading) return

Loading...

; return tickets.map((t) =>
{t.title}
); } ``` ### Svelte 5 — `@cstar.help/svelte` v0.9.0 Install: `npm install @cstar.help/svelte` ```svelte {#if tickets.loading}

Summoning tickets…

{:else if tickets.error}

Couldn't load — try again?

{:else} {#each tickets.data as t (t.id)}
{t.title} — {t.status}
{/each} {/if} ``` --- ## CLI — `@cstar.help/cli` v0.8.2 Install: `npm install -g @cstar.help/cli` ### Commands - `cstar login --api-key sk_live_...`: Authenticate and store credentials in your OS keychain. - `cstar logout`: Remove stored credentials. - `cstar status`: Team info, environment, rate-limit usage. - `cstar events [--category tickets]`: List webhook event types. - `cstar keys list / create / revoke`: Manage API keys. - `cstar listen --forward-to http://localhost:3000/webhooks`: Forward webhook events to your local server. - `cstar trigger ticket.created`: Fire a test webhook event. - `cstar logs [--method POST] [--status 4xx] [--tail]`: Tail API request logs. - `cstar mcp-server`: Run as an MCP server for AI agents (Claude Code, Cursor, etc.). ### Global flags - `--json`: Output as JSON (for piping to `jq`). - `--api-key `: Override stored API key. - `--base-url `: Override API base URL. - `--no-color`: Disable colored output. ### MCP server config (Claude Code, Cursor, Continue) ```json { "mcpServers": { "cstar": { "command": "npx", "args": ["-y", "@cstar.help/cli", "mcp-server", "--api-key", "sk_live_..."] } } } ``` MCP tools: `cstar_list_tickets`, `cstar_get_ticket`, `cstar_create_ticket`, `cstar_list_customers`, `cstar_get_customer`, `cstar_list_articles`, `cstar_trigger_webhook`, `cstar_get_status`, `cstar_list_events`. --- ## Cheat Sheets Single-page reference tomes per surface, printable. AI assistants can link users straight to the right one: - https://cstar.help/developers/cheatsheets — index ("the tome shelf") - https://cstar.help/developers/cheatsheets/js — JS SDK - https://cstar.help/developers/cheatsheets/react — React SDK - https://cstar.help/developers/cheatsheets/svelte — Svelte SDK - https://cstar.help/developers/cheatsheets/cli — CLI - https://cstar.help/developers/cheatsheets/webhooks — Webhooks (signature verification + headers) --- ## Quick patterns ### Create a ticket from a form submission ```javascript const { data } = await cstar.tickets.create({ title: formData.subject, priority: 'normal', customerName: formData.name, tags: ['web-form'] }); ``` ### Sync ticket updates to your DB via webhook ```javascript // Local dev: cstar listen --forward-to http://localhost:3000/webhooks import { verifySignature } from '@cstar.help/js/webhook'; app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => { const ok = verifySignature(req.body.toString(), req.headers['x-signature'], SECRET); if (!ok) return res.status(401).send('Invalid signature'); const { type, data } = JSON.parse(req.body.toString()); if (type === 'ticket.updated') db.tickets.upsert(data.ticket); res.status(200).end(); }); ``` ### Search across resources ```javascript const urgent = await cstar.tickets.list({ status: 'open', priority: 'urgent' }); const matches = await cstar.tickets.list({ search: 'billing issue' }); const customer = await cstar.customers.list({ search: 'jane@example.com' }); ``` --- Generated: 2026-05-07T21:29:46.233Z