Endpoint reference

Every endpoint below is prefixed with https://octopuslabapi-production.up.railway.app/api/v1 and gated by an API key from Settings → API keys. See Authentication for the header shape.

GET /public/me

Returns the org bound to the calling key plus the key's scopes.

curl -H "Authorization: Bearer ok_…" \
  https://…/api/v1/public/me
{
  "org": {
    "id": "edac38fa-3b59-4373-8485-f68be75bc6dc",
    "slug": "aurora-labs",
    "name": "Aurora Labs",
    "plan": "pro"
  },
  "scopes": ["*"]
}

GET /public/projects

Lists this org's projects, newest first. Capped at 200 rows.

curl -H "Authorization: Bearer ok_…" \
  https://…/api/v1/public/projects
[
  {
    "id": "811792d9-0f73-4651-b6e6-4479f667c6e1",
    "slug": "a-landing-page-for-80ry",
    "name": "A landing page for",
    "status": "live",
    "createdAt": "2026-06-04T11:00:00Z",
    "updatedAt": "2026-06-05T12:00:00Z"
  }
]

GET /public/projects/:id

Single project by id. 404 if it isn't in this org.

POST /public/projects

Creates a new project from a prompt. The prompt becomes the first user message of the project's conversation — the AI generation kicks off asynchronously, so the response comes back with status: "generating" and you should poll GET /public/projects/:id until it flips to live / failed.

curl -X POST \
  -H "Authorization: Bearer ok_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pricing experiments",
    "slug": "pricing-experiments",
    "prompt": "A landing page comparing three pricing plans for a SaaS, dark theme."
  }' \
  https://…/api/v1/public/projects

Request body:

  • name (string, 1–80 chars) — human-readable title shown in the dashboard.
  • slug (string, lowercase letters / digits / hyphens, 1–63 chars) — must be unique inside the org. Becomes the production subdomain.
  • prompt (string, 1–8000 chars) — first user message.
  • stack (optional, "next" | "astro" | "react" | "static") — defaults to "next".

On 403 "Project with this slug already exists in the org" change the slug and retry — we don't auto-disambiguate (so your script doesn't accidentally generate twice).

GET /public/projects/:id/deployments

50 most recent deployments for the project, newest first.

[
  {
    "id": "1e71724f-…",
    "shortId": "ab7f0c0",
    "status": "ready",
    "commitMessage": "Manual promote to production",
    "createdAt": "2026-06-08T19:00:00Z",
    "isActiveProd": true
  }
]

isActiveProd: true marks the deployment currently aliased to your custom domain / subdomain. Exactly one row at a time has it set.

GET /public/projects/:id/audits

50 most recent SEO/AEO audits for the project.

[
  {
    "id": "…",
    "url": "https://a-landing-page-for-80ry.octopus-lab.app",
    "status": "done",
    "overallScore": 92,
    "startedAt": "…",
    "finishedAt": "…"
  }
]

Coming in v1.1

  • POST /public/projects/:id/deploy — trigger a redeploy.
  • POST /public/projects/:id/audits/run — trigger an audit.
  • GET /public/projects/:id/audits/:auditId — the findings list.
  • POST /public/projects/:id/messages — append a chat message programmatically (and stream the AI's reply via SSE).