page.tsx449 lines · main
| 1 | import { DocsShell } from '../../components/shell'; |
| 2 | |
| 3 | export const metadata = { title: 'http api' }; |
| 4 | |
| 5 | interface Endpoint { |
| 6 | method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT'; |
| 7 | path: string; |
| 8 | summary: string; |
| 9 | } |
| 10 | |
| 11 | interface Section { |
| 12 | title: string; |
| 13 | intro: string; |
| 14 | endpoints: Endpoint[]; |
| 15 | } |
| 16 | |
| 17 | const SECTIONS: readonly Section[] = [ |
| 18 | { |
| 19 | title: 'invoke', |
| 20 | intro: |
| 21 | 'Call a deployed function. Auth: project API key (brk_…) in Authorization: Bearer header.', |
| 22 | endpoints: [ |
| 23 | { |
| 24 | method: 'POST', |
| 25 | path: '/v1/projects/:id/invoke', |
| 26 | summary: |
| 27 | 'Body: { function: string, args?: unknown }. Returns { result, durationMs } or { error }. Counts against your project\'s monthly invocation cap.', |
| 28 | }, |
| 29 | ], |
| 30 | }, |
| 31 | { |
| 32 | title: 'realtime', |
| 33 | intro: |
| 34 | 'Subscribe to reactive queries over WebSocket. SDK clients (@briven/react / svelte / vue) wrap this.', |
| 35 | endpoints: [ |
| 36 | { |
| 37 | method: 'GET', |
| 38 | path: '/v1/projects/:id/realtime', |
| 39 | summary: |
| 40 | 'WSS upgrade. Send { type: "subscribe", function, args }, receive frames as the underlying rows change. Auth: project API key.', |
| 41 | }, |
| 42 | ], |
| 43 | }, |
| 44 | { |
| 45 | title: 'projects', |
| 46 | intro: 'Project CRUD. Auth: dashboard session cookie.', |
| 47 | endpoints: [ |
| 48 | { method: 'GET', path: '/v1/projects', summary: 'List every project the caller can see.' }, |
| 49 | { |
| 50 | method: 'POST', |
| 51 | path: '/v1/projects', |
| 52 | summary: |
| 53 | 'Body: { name, slug?, region?, orgId? }. Creates a fresh schema + runtime under the named org (defaults to your personal org).', |
| 54 | }, |
| 55 | { method: 'GET', path: '/v1/projects/:id', summary: 'Project details.' }, |
| 56 | { |
| 57 | method: 'PATCH', |
| 58 | path: '/v1/projects/:id', |
| 59 | summary: 'Body: { name?, slug? }. Rename a project.', |
| 60 | }, |
| 61 | { |
| 62 | method: 'POST', |
| 63 | path: '/v1/projects/:id/move', |
| 64 | summary: |
| 65 | 'Body: { orgId }. Re-parent to a different org (you must be a member of both).', |
| 66 | }, |
| 67 | { |
| 68 | method: 'DELETE', |
| 69 | path: '/v1/projects/:id', |
| 70 | summary: |
| 71 | 'Soft-delete. The schema and data are retained for 30 days before hard deletion.', |
| 72 | }, |
| 73 | ], |
| 74 | }, |
| 75 | { |
| 76 | title: 'deployments', |
| 77 | intro: 'Bundle uploads + history. Auth: project API key (POST) or session (GET).', |
| 78 | endpoints: [ |
| 79 | { |
| 80 | method: 'POST', |
| 81 | path: '/v1/projects/:id/deployments', |
| 82 | summary: |
| 83 | 'Upload a schema + functions bundle. Body: multipart form with bundle.tar.gz. Server diffs against the live schema, generates a migration, applies transactionally.', |
| 84 | }, |
| 85 | { |
| 86 | method: 'GET', |
| 87 | path: '/v1/projects/:id/deployments', |
| 88 | summary: 'List recent deployments with their status + schema diff summary.', |
| 89 | }, |
| 90 | { |
| 91 | method: 'GET', |
| 92 | path: '/v1/projects/:id/deployments/:deploymentId', |
| 93 | summary: 'Deployment detail including error code/message if it failed.', |
| 94 | }, |
| 95 | { |
| 96 | method: 'POST', |
| 97 | path: '/v1/projects/:id/deployments/:deploymentId/cancel', |
| 98 | summary: 'Cancel a pending or running deployment.', |
| 99 | }, |
| 100 | ], |
| 101 | }, |
| 102 | { |
| 103 | title: 'studio', |
| 104 | intro: |
| 105 | 'Dashboard data browser + DDL surface. Auth: dashboard session, admin role on the project.', |
| 106 | endpoints: [ |
| 107 | { method: 'GET', path: '/v1/projects/:id/studio/tables', summary: 'List user tables.' }, |
| 108 | { |
| 109 | method: 'POST', |
| 110 | path: '/v1/projects/:id/studio/tables', |
| 111 | summary: |
| 112 | 'Body: { tableName, columns: [{ name, type, notNull?, primaryKey?, defaultExpr?, references? }] }. CREATE TABLE.', |
| 113 | }, |
| 114 | { |
| 115 | method: 'PATCH', |
| 116 | path: '/v1/projects/:id/studio/tables/:table', |
| 117 | summary: 'Body: { newName }. Rename a table.', |
| 118 | }, |
| 119 | { |
| 120 | method: 'POST', |
| 121 | path: '/v1/projects/:id/studio/tables/:table/truncate', |
| 122 | summary: 'Body: { cascade? }. TRUNCATE TABLE … RESTART IDENTITY.', |
| 123 | }, |
| 124 | { |
| 125 | method: 'DELETE', |
| 126 | path: '/v1/projects/:id/studio/tables/:table', |
| 127 | summary: 'DROP TABLE.', |
| 128 | }, |
| 129 | { |
| 130 | method: 'GET', |
| 131 | path: '/v1/projects/:id/studio/tables/:table/columns', |
| 132 | summary: 'Per-column metadata with PK + FK target.', |
| 133 | }, |
| 134 | { |
| 135 | method: 'POST', |
| 136 | path: '/v1/projects/:id/studio/tables/:table/columns', |
| 137 | summary: 'Body: { column: { name, type, notNull?, defaultExpr?, references? } }. ADD COLUMN.', |
| 138 | }, |
| 139 | { |
| 140 | method: 'PATCH', |
| 141 | path: '/v1/projects/:id/studio/tables/:table/columns/:column', |
| 142 | summary: |
| 143 | 'Two-mode: { newName } to rename, or { notNull?, defaultExpr? } to alter nullability / default.', |
| 144 | }, |
| 145 | { |
| 146 | method: 'DELETE', |
| 147 | path: '/v1/projects/:id/studio/tables/:table/columns/:column', |
| 148 | summary: 'DROP COLUMN.', |
| 149 | }, |
| 150 | { |
| 151 | method: 'GET', |
| 152 | path: '/v1/projects/:id/studio/tables/:table/indexes', |
| 153 | summary: 'List indexes on a table.', |
| 154 | }, |
| 155 | { |
| 156 | method: 'POST', |
| 157 | path: '/v1/projects/:id/studio/tables/:table/indexes', |
| 158 | summary: 'Body: { columns: string[], unique?, name? }. CREATE INDEX.', |
| 159 | }, |
| 160 | { |
| 161 | method: 'DELETE', |
| 162 | path: '/v1/projects/:id/studio/tables/:table/indexes/:name', |
| 163 | summary: 'DROP INDEX (refuses the primary-key index).', |
| 164 | }, |
| 165 | { |
| 166 | method: 'GET', |
| 167 | path: '/v1/projects/:id/studio/tables/:table/rows', |
| 168 | summary: |
| 169 | 'Paginated rows. Query: limit, offset, orderBy + dir, and per-column <col>__eq=value filters.', |
| 170 | }, |
| 171 | { |
| 172 | method: 'POST', |
| 173 | path: '/v1/projects/:id/studio/tables/:table/rows', |
| 174 | summary: 'Body: { values: { col: value, … } }. Insert a row.', |
| 175 | }, |
| 176 | { |
| 177 | method: 'PATCH', |
| 178 | path: '/v1/projects/:id/studio/tables/:table/rows', |
| 179 | summary: |
| 180 | 'Body: { primaryKeyColumn, primaryKeyValue, column, value }. Update a single cell.', |
| 181 | }, |
| 182 | { |
| 183 | method: 'DELETE', |
| 184 | path: '/v1/projects/:id/studio/tables/:table/rows', |
| 185 | summary: 'Body: { primaryKeyColumn, primaryKeyValue }. Delete a row.', |
| 186 | }, |
| 187 | { |
| 188 | method: 'POST', |
| 189 | path: '/v1/projects/:id/studio/query', |
| 190 | summary: |
| 191 | 'Body: { sql }. Run arbitrary SQL scoped to the project owner role. 5s statement_timeout. Audit-logged.', |
| 192 | }, |
| 193 | { |
| 194 | method: 'GET', |
| 195 | path: '/v1/projects/:id/studio/schema', |
| 196 | summary: 'One-shot read of every table + columns + FK edges.', |
| 197 | }, |
| 198 | { |
| 199 | method: 'GET', |
| 200 | path: '/v1/projects/:id/studio/schema.ts', |
| 201 | summary: |
| 202 | 'Generates the equivalent briven/schema.ts so a dashboard-built database can graduate to git + CLI.', |
| 203 | }, |
| 204 | { |
| 205 | method: 'GET', |
| 206 | path: '/v1/projects/:id/studio/relationships', |
| 207 | summary: 'Every FK edge in the schema (used by the studio overview).', |
| 208 | }, |
| 209 | ], |
| 210 | }, |
| 211 | { |
| 212 | title: 'logs + stats', |
| 213 | intro: 'Function invocation history. Auth: dashboard session.', |
| 214 | endpoints: [ |
| 215 | { |
| 216 | method: 'GET', |
| 217 | path: '/v1/projects/:id/function-logs', |
| 218 | summary: 'Query: function, status (ok|err), before (cursor), limit.', |
| 219 | }, |
| 220 | { |
| 221 | method: 'GET', |
| 222 | path: '/v1/projects/:id/function-names', |
| 223 | summary: 'Distinct function names actually called in this project.', |
| 224 | }, |
| 225 | { |
| 226 | method: 'GET', |
| 227 | path: '/v1/projects/:id/function-stats', |
| 228 | summary: |
| 229 | 'Query: function (required), hours (default 24). Returns count + errCount + p50Ms + p99Ms.', |
| 230 | }, |
| 231 | { |
| 232 | method: 'GET', |
| 233 | path: '/v1/projects/:id/hourly-invocations', |
| 234 | summary: '24-hour series of invocation counts per hour (used by the project sparkline).', |
| 235 | }, |
| 236 | { |
| 237 | method: 'GET', |
| 238 | path: '/v1/projects/:id/logs/stream', |
| 239 | summary: 'SSE stream of new invocations as they arrive.', |
| 240 | }, |
| 241 | ], |
| 242 | }, |
| 243 | { |
| 244 | title: 'usage', |
| 245 | intro: 'Metering. Auth: project API key or session.', |
| 246 | endpoints: [ |
| 247 | { |
| 248 | method: 'GET', |
| 249 | path: '/v1/projects/:id/usage', |
| 250 | summary: 'Current period: invocations.count + totalDurationMs, storage.bytes, limits.', |
| 251 | }, |
| 252 | ], |
| 253 | }, |
| 254 | { |
| 255 | title: 'api keys', |
| 256 | intro: 'Per-project deploy / invoke keys.', |
| 257 | endpoints: [ |
| 258 | { method: 'GET', path: '/v1/projects/:id/api-keys', summary: 'List keys (hashes only).' }, |
| 259 | { |
| 260 | method: 'POST', |
| 261 | path: '/v1/projects/:id/api-keys', |
| 262 | summary: |
| 263 | 'Body: { name, scope }. Returns the plaintext key once — record it then; never retrievable again.', |
| 264 | }, |
| 265 | { |
| 266 | method: 'PATCH', |
| 267 | path: '/v1/projects/:id/api-keys/:keyId', |
| 268 | summary: 'Rename a key.', |
| 269 | }, |
| 270 | { |
| 271 | method: 'DELETE', |
| 272 | path: '/v1/projects/:id/api-keys/:keyId', |
| 273 | summary: 'Revoke a key.', |
| 274 | }, |
| 275 | ], |
| 276 | }, |
| 277 | { |
| 278 | title: 'project members + invitations', |
| 279 | intro: 'Per-project access control (separate from org membership).', |
| 280 | endpoints: [ |
| 281 | { |
| 282 | method: 'GET', |
| 283 | path: '/v1/projects/:id/members', |
| 284 | summary: 'List members + roles.', |
| 285 | }, |
| 286 | { |
| 287 | method: 'POST', |
| 288 | path: '/v1/projects/:id/invitations', |
| 289 | summary: 'Body: { email, role, callbackURL }. Sends an email with a one-time accept link.', |
| 290 | }, |
| 291 | { |
| 292 | method: 'GET', |
| 293 | path: '/v1/me/invitations', |
| 294 | summary: 'Pending project invitations for the signed-in user.', |
| 295 | }, |
| 296 | { |
| 297 | method: 'POST', |
| 298 | path: '/v1/me/invitations/accept', |
| 299 | summary: 'Body: { token }. Accept by token from the email link.', |
| 300 | }, |
| 301 | ], |
| 302 | }, |
| 303 | { |
| 304 | title: 'orgs (teams)', |
| 305 | intro: 'Multi-org workspace. Personal org is auto-created on signup.', |
| 306 | endpoints: [ |
| 307 | { method: 'GET', path: '/v1/me/orgs', summary: 'Every org the caller belongs to.' }, |
| 308 | { |
| 309 | method: 'POST', |
| 310 | path: '/v1/orgs', |
| 311 | summary: |
| 312 | 'Body: { name, slug? }. Create a team org (paid tiers only — free tier caps at the personal org).', |
| 313 | }, |
| 314 | { |
| 315 | method: 'PATCH', |
| 316 | path: '/v1/orgs/:id', |
| 317 | summary: 'Body: { name }. Rename a team org.', |
| 318 | }, |
| 319 | { |
| 320 | method: 'DELETE', |
| 321 | path: '/v1/orgs/:id', |
| 322 | summary: |
| 323 | 'Soft-delete a team org. Refuses while live projects still belong to it; move or delete them first.', |
| 324 | }, |
| 325 | { |
| 326 | method: 'GET', |
| 327 | path: '/v1/orgs/:id/members', |
| 328 | summary: 'List org members with roles.', |
| 329 | }, |
| 330 | { |
| 331 | method: 'PATCH', |
| 332 | path: '/v1/orgs/:id/members/:userId', |
| 333 | summary: 'Body: { role }. Change role. Refuses to demote the last owner.', |
| 334 | }, |
| 335 | { |
| 336 | method: 'DELETE', |
| 337 | path: '/v1/orgs/:id/members/:userId', |
| 338 | summary: 'Remove a member.', |
| 339 | }, |
| 340 | { |
| 341 | method: 'GET', |
| 342 | path: '/v1/orgs/:id/invitations', |
| 343 | summary: 'Pending org invitations.', |
| 344 | }, |
| 345 | { |
| 346 | method: 'POST', |
| 347 | path: '/v1/orgs/:id/invitations', |
| 348 | summary: 'Body: { email, role, callbackURL }. Invite a collaborator.', |
| 349 | }, |
| 350 | { |
| 351 | method: 'POST', |
| 352 | path: '/v1/org-invitations/accept', |
| 353 | summary: 'Body: { token }.', |
| 354 | }, |
| 355 | ], |
| 356 | }, |
| 357 | { |
| 358 | title: 'billing', |
| 359 | intro: 'Polar.sh-backed subscription management.', |
| 360 | endpoints: [ |
| 361 | { |
| 362 | method: 'GET', |
| 363 | path: '/v1/billing/subscription', |
| 364 | summary: 'Current tier, status, period end, polar customer id.', |
| 365 | }, |
| 366 | { |
| 367 | method: 'GET', |
| 368 | path: '/v1/billing/plans', |
| 369 | summary: 'Public plan + price list (matches docs.briven.tech/pricing).', |
| 370 | }, |
| 371 | { |
| 372 | method: 'POST', |
| 373 | path: '/v1/billing/checkout', |
| 374 | summary: |
| 375 | 'Body: { tier, successURL }. Returns { url } — hosted Polar checkout URL.', |
| 376 | }, |
| 377 | { |
| 378 | method: 'POST', |
| 379 | path: '/v1/billing/portal', |
| 380 | summary: 'Returns { url } for the Polar customer portal (manage payment / cancel).', |
| 381 | }, |
| 382 | { |
| 383 | method: 'POST', |
| 384 | path: '/v1/billing/webhook', |
| 385 | summary: |
| 386 | 'Polar.sh webhook receiver. HMAC-validated. Server-side only — your app doesn\'t call this.', |
| 387 | }, |
| 388 | ], |
| 389 | }, |
| 390 | ]; |
| 391 | |
| 392 | export default function ApiPage() { |
| 393 | return ( |
| 394 | <DocsShell> |
| 395 | <h1 className="font-mono text-2xl tracking-tight">http api</h1> |
| 396 | <p className="mt-2 font-mono text-sm text-[var(--color-text-muted)]"> |
| 397 | every dashboard surface is built on these endpoints; you can build your own client |
| 398 | on top of them too. base url <code>https://api.briven.tech</code>. all responses are |
| 399 | JSON unless otherwise noted. auth is either a dashboard session cookie (Better Auth) |
| 400 | or a project API key in <code>Authorization: Bearer brk_…</code>; each section calls |
| 401 | out which. |
| 402 | </p> |
| 403 | |
| 404 | <p className="mt-4 font-mono text-sm text-[var(--color-text-muted)]"> |
| 405 | admin-only routes under <code>/v1/admin/*</code> aren't listed here — they need |
| 406 | platform-admin and are documented in the operator runbook. |
| 407 | </p> |
| 408 | |
| 409 | {SECTIONS.map((sec) => ( |
| 410 | <section key={sec.title} className="mt-10"> |
| 411 | <h2 className="font-mono text-lg tracking-tight">{sec.title}</h2> |
| 412 | <p className="mt-1 font-mono text-sm text-[var(--color-text-muted)]">{sec.intro}</p> |
| 413 | <ul className="mt-4 flex flex-col gap-2"> |
| 414 | {sec.endpoints.map((e, i) => ( |
| 415 | <li |
| 416 | key={i} |
| 417 | className="rounded-md border border-[var(--color-border-subtle)] bg-[var(--color-surface)] p-3 font-mono text-xs" |
| 418 | > |
| 419 | <p> |
| 420 | <span |
| 421 | className={`mr-2 inline-block w-12 rounded-sm px-1.5 py-0.5 text-center text-[10px] ${methodColour(e.method)}`} |
| 422 | > |
| 423 | {e.method} |
| 424 | </span> |
| 425 | <code className="text-[var(--color-text)]">{e.path}</code> |
| 426 | </p> |
| 427 | <p className="mt-1 text-[var(--color-text-muted)]">{e.summary}</p> |
| 428 | </li> |
| 429 | ))} |
| 430 | </ul> |
| 431 | </section> |
| 432 | ))} |
| 433 | </DocsShell> |
| 434 | ); |
| 435 | } |
| 436 | |
| 437 | function methodColour(m: Endpoint['method']): string { |
| 438 | switch (m) { |
| 439 | case 'GET': |
| 440 | return 'bg-[var(--color-primary-subtle)] text-[var(--color-primary)]'; |
| 441 | case 'POST': |
| 442 | return 'bg-emerald-400/15 text-emerald-400'; |
| 443 | case 'PATCH': |
| 444 | case 'PUT': |
| 445 | return 'bg-amber-400/15 text-amber-400'; |
| 446 | case 'DELETE': |
| 447 | return 'bg-red-400/15 text-red-400'; |
| 448 | } |
| 449 | } |