API Reference

A REST API for managing projects and running jobs. Every endpoint lives under /api/v1 and authenticates with a per-user API key.

The same operations are available to AI agents through the MCP server.

Authentication

Generate a key in Settings. Pass it as a Bearer token. Keys are shown once at creation and stored hashed — generating a new key revokes the previous one.

curl https://lanceo.daho.ai/api/v1/me \
  -H "Authorization: Bearer sk_..."

Requests are rate limited to 60 per minute per user. Exceeding it returns 429.

Responses

Successful responses wrap the payload in data. Errors carry a machine-readable code and a human-readable message.

{ "data": { "id": 1, "name": "My project" } }

{ "error": { "code": "NOT_FOUND", "message": "Project not found" } }
StatusMeaning
401Missing or invalid API key
402Out of credits
404Not found, or not yours
409Duplicate project name
429Rate limit exceeded

Account

GET/api/v1/me

The authenticated user's profile, project count, and remaining credits.

{
  "data": {
    "id": "usr_...",
    "name": "Ada Lovelace",
    "email": "[email protected]",
    "role": "user",
    "billingModel": "credits",
    "usage": {
      "projects": { "current": 3, "limit": null },
      "credits": { "current": 27, "costs": { "market_suggest": 1 } }
    }
  }
}

Projects

A project groups jobs and assistant sessions. The metadata object is free-form — use it for your own domain fields without a migration.

GET/api/v1/projects

List your projects, most recently updated first.

POST/api/v1/projects

Create a project. The slug is derived from the name; a duplicate name returns 409.

curl -X POST https://lanceo.daho.ai/api/v1/projects \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "My project", "description": "Optional"}'
GET/api/v1/projects/:projectId

Fetch one project.

PATCH/api/v1/projects/:projectId

Update name, description, status (active or archived), or metadata.

curl -X PATCH https://lanceo.daho.ai/api/v1/projects/1 \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{"status": "archived", "metadata": {"tier": "pro"}}'
DELETE/api/v1/projects/:projectId

Delete a project and cascade-delete its jobs. Assistant sessions are kept but unlinked.

Jobs

Jobs run asynchronously and cost credits by type. Starting a job returns immediately with an id — poll for the result. If the handler fails, the job is marked failed and the credits are refunded automatically.

POST/api/v1/jobs

Start a job. Returns 402 if you are out of credits, or 400 for an unknown type.

curl -X POST https://lanceo.daho.ai/api/v1/jobs \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "projectId": 1,
    "type": "summarize",
    "input": { "text": "Some text to summarize." }
  }'

{ "data": { "jobId": 42, "status": "queued" } }
GET/api/v1/jobs

List your jobs, newest first. Add ?projectId= to filter to one project.

GET/api/v1/jobs/:jobId

Fetch a job's status and result. Status is queued, running, completed, or failed.

{
  "data": {
    "id": 42,
    "projectId": 1,
    "type": "summarize",
    "status": "completed",
    "input": { "text": "..." },
    "result": { "summary": "...", "keyPoints": ["..."] },
    "error": null,
    "startedAt": "2026-09-01T12:00:01Z",
    "finishedAt": "2026-09-01T12:00:09Z"
  }
}

Job types

The template ships one job type, summarize, which takes { text: string } and returns { summary, keyPoints }. It exists to demonstrate the pipeline and is meant to be replaced.

Register your own by adding an entry to JOB_HANDLERS in lib/jobs/handlers.ts. Nothing else needs to change — the credit accounting, refund path, completion email, and this endpoint all work off the registry.

export const JOB_HANDLERS: Record<string, JobHandler> = {
  summarize,
  myJob: async (input) => {
    const { url } = myJobInput.parse(input);
    return { processed: await doTheWork(url) };
  },
};