Skip to content

REST API

Use the Goodeye REST API to integrate workflows, templates, verifiers, and image generators directly into your application or automation pipeline.

Base URL

https://api.goodeye.dev/v1

Interactive API docs are available at https://api.goodeye.dev/v1/docs.

Authentication

Most endpoints require a good_live_ API key. Pass it as a Bearer token:

Authorization: Bearer good_live_EXAMPLE_xxxxxxxx

Create a key with goodeye auth create-key or POST /v1/api-keys after signing in. Keys are shown once at creation time; store them securely.

Anonymous access: the public template catalog (GET /v1/templates, GET /v1/templates/{identifier}, POST /v1/templates/search) is readable without authentication. Anonymous callers also have a small monthly credit grant for metered operations such as POST /v1/verifiers/{id}/runs against publicly referenced verifiers.

Bootstrap and registration

Before a user has an API key, two endpoints let clients register and log in.

GET /.well-known/goodeye-client-config

No auth required. Returns the default bundle ignore list (applied when pushing a workflow directory) and the configuration the CLI uses to begin an interactive sign-in. This is the first call the CLI makes on a fresh install. For programmatic access, authenticate with an API key (see below) rather than the interactive sign-in flow.

GET /.well-known/goodeye-client-config
{
  "ignore_defaults": ["*.pyc", ".DS_Store", "..."]
}

Registration and login (email code flow)

POST /v1/register
Content-Type: application/json

{"email": "you@example.com"}
HTTP/1.1 202 Accepted
{"status": "ok", "message": "Check your email for next steps."}

Then verify the code from your inbox:

POST /v1/register/verify
Content-Type: application/json

{"email": "you@example.com", "code": "123456"}
{"api_key": "good_live_..."}

Use POST /v1/login and POST /v1/login/verify for returning accounts (same flow). The verify response includes your API key, which you can use for all subsequent requests.

OAuth token exchange

MCP clients that completed an OAuth sign-in call POST /v1/auth/exchange to receive a host-scoped CLI key. This is handled automatically by the CLI; direct callers generally do not need it.

Pagination

List endpoints return a cursor-paginated response:

{
  "items": [...],
  "next_cursor": "eyJjIjoiMjAyNC0wMS0wMVQwMDowMDowMFoiLCJpIjoiYWJjIn0"
}

Pass next_cursor as the cursor query parameter on the next request. When next_cursor is null, you are on the last page. Most list endpoints also accept a limit parameter.

Error model

All errors return JSON with a stable error slug and a human-readable message. Some errors include a hint field with recovery guidance, and retryable when relevant.

{
  "error": "not_found",
  "message": "workflow not found"
}
HTTP statusSlugWhen it occurs
400validation_errorBad request shape or parameter
400handle_invalidHandle format rejected
401auth_requiredMissing or invalid credentials
401invalid_credentialsBad email code at login/register
402budget_exhaustedMonthly credit grant spent
403account_suspendedAccount suspended
403forbiddenCaller lacks the required role
404not_foundResource not found or masked (private resources are masked as not-found for unauthorized callers)
404user_not_foundTarget user does not exist
409conflictDuplicate, locked, or serving-gate refusal
409handle_takenHandle already claimed by another user
409handle_reservedHandle is reserved and cannot be claimed
409handle_already_claimedCaller already has a handle
409handle_not_claimedA handle is required before performing this action
409invitation_already_resolvedInvitation was already accepted, declined, or cancelled
409invitation_already_pendingAn equivalent invitation is already pending
410invitation_expiredInvitation has expired
422safety_verification_failedTemplate body failed the hard-block safety check at publish
422quota_exceededA per-user quota was hit
429rate_limitedRequest rate limit exceeded
429handle_rename_too_soonHandle renamed within the 90-day cooldown
429handle_rename_limit_exceededHandle rename calendar-year limit reached
503safety_verification_unavailableSafety verifier runtime unavailable at publish time; retry
503search_unavailableLLM-ranked search temporarily unavailable; use list with search= for lexical fallback
503verifier_unavailableVerifier LLM runtime unavailable; retry the run

safety_verification_failed responses include reasoning (what to change) and verifier_run_id for audit.

Account and profile

GET /v1/me

Returns the authenticated caller's account id, email, and handle.

GET /v1/me
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
{
  "id": "usr_...",
  "email": "you@example.com",
  "handle": "yourhandle",
  "handle_claimed_at": "2024-06-01T12:00:00Z"
}

PATCH /v1/me

Claim a handle.

PATCH /v1/me
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{"handle": "yourhandle"}

POST /v1/me/rename-handle

Rename your claimed handle. Limited to once per rolling 90 days and 3 times per calendar year.

POST /v1/me/rename-handle
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{"handle": "newhandle"}

GET /v1/me/usage

Returns your current billing period usage: tier, period dates, granted/used/remaining credits, and unpaid balance.

GET /v1/me/usage
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx

See Accounts and billing for tier details.

API keys

POST /v1/api-keys

Mint a new API key.

POST /v1/api-keys
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{"name": "my-automation"}
{
  "id": "key_...",
  "name": "my-automation",
  "key": "good_live_..."
}

The key field is only returned once. Store it immediately.

GET /v1/api-keys

List your keys (metadata only, no secrets).

DELETE /v1/api-keys/{key_id}

Revoke a key. Returns 204 No Content. A second delete returns 404.

Workflows

Workflows are private to their owner. Use templates to share publicly.

POST /v1/workflows

Create or update a workflow. Required fields: name, body. Optional: description, outcome, tags, slug, files (multi-file bundle).

POST /v1/workflows
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{
  "name": "Weekly report",
  "outcome": "Deliver a concise weekly summary to the team",
  "body": "# Weekly report\n\n...",
  "tags": ["reporting"]
}
{
  "workflow_id": "wf_...",
  "version": 1,
  "version_token": "...",
  "name": "Weekly report",
  "slug": "weekly-report",
  "verifiers": []
}

GET /v1/workflows

List workflows visible to you. Query params: filter (mine, all, shared-with-me), tag, search, limit, cursor, include_archived.

POST /v1/workflows/search

Natural language search. Body: {"query": "..."}.

GET /v1/workflows/{id_or_slug}

Fetch a workflow. Pass ?version=N to pin a specific version. Add Accept: text/markdown to receive the body as plain markdown (the agent's runbook fetch path).

GET /v1/workflows/{id_or_slug}/versions/{version}

Fetch a specific version directly.

GET /v1/workflows/{id_or_slug}/files

Fetch files from a workflow bundle. Use ?path=relative/path.md for a single file or ?paths=a.md&paths=b.md for a batch.

POST /v1/workflows/{id_or_slug}/archive

Reversibly hide a workflow. The slug stays occupied and the workflow is recoverable.

POST /v1/workflows/{id_or_slug}/unarchive

Restore an archived workflow.

DELETE /v1/workflows/{id_or_slug}

Permanently erase a workflow and all its versions. No recovery. Works on live and archived workflows.

DELETE /v1/workflows/{id_or_slug}/versions/{n}

Permanently erase a single non-current version. The current version cannot be erased this way; use DELETE /v1/workflows/{id_or_slug} to remove everything.

POST /v1/workflows/{id_or_slug}/teach

Teach an existing workflow with new examples.

POST /v1/workflows/{id_or_slug}/optimize

Run an iterative optimization loop. Optional query param max_iterations (1 to 1000).

POST /v1/workflows/{id_or_slug}/safety-check

Run both platform safety checks on a workflow version. Requires auth; bills two metered runs.

Grants

POST   /v1/workflows/{id_or_slug}/grants      # grant access
GET    /v1/workflows/{id_or_slug}/grants      # list grants
DELETE /v1/workflows/{id_or_slug}/grants      # revoke a grant
POST   /v1/workflows/{id_or_slug}/leave       # leave a shared workflow

Grant body: {"grantee_email_or_at_team_handle": "...", "role": "view|edit|admin", "include_history": false}.

Revoke body: {"grantee_email_or_at_team_handle": "..."}.

POST /v1/workflows/{id_or_slug}/transfer-ownership

Transfer ownership to another user. Returns an invitation envelope; the recipient calls POST /v1/invitations/{id}/accept to apply.

GET /v1/workflows/{id_or_slug}/lineage

Trace a workflow back to its template fork source.

Templates

Templates are the public form of a workflow. Anonymous callers can list, search, and read templates.

POST /v1/templates

Publish a workflow as a new template version. Runs safety checks before any writes; returns safety_verification with status and optional advisory_reasoning.

Errors: safety_verification_failed (422) on a hard block, safety_verification_unavailable (503) if the runtime is unreachable.

POST /v1/templates
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{
  "workflow_id": "wf_...",
  "release_notes": "Initial release"
}

GET /v1/templates

List public templates. Query params: filter (all, mine), search, limit, cursor, include_archived (only your own archived templates when authenticated).

POST /v1/templates/search

Natural language search. Body: {"query": "..."}. Anonymous callers may search.

POST /v1/templates/fork

Fork a template into a private workflow. Body: {"identifier": "@handle/slug", "version": null, "name": null}. Works for anonymous callers (creates an ephemeral workflow) or authenticated users (creates a persisted workflow).

GET /v1/templates/{identifier}

Fetch a template. identifier is a UUID or @handle/slug (optionally @handle/slug@vN). Query param: version. Add Accept: text/markdown to receive the body directly.

Note: non-owners see a safety status banner prepended to the body. The banner is neutral: it shows the publishing handle and safety status only.

GET /v1/templates/{identifier}/files

Fetch a single file from a template version's file tree. Query param: path.

Version management

DELETE /v1/templates/{identifier}/versions/{v}             # unpublish a version
DELETE /v1/templates/{identifier}/versions/{v}/permanent   # permanently erase (must unpublish first)
POST   /v1/templates/{identifier}/versions/{v}/deprecate   # flag as deprecated without hiding

POST /v1/templates/{identifier}/safety-check

Run platform safety checks on a template version. Auth optional; anonymous callers are billed against their per-IP grant.

POST /v1/templates/{identifier}/archive

Reversibly hide from public listing. Optional body: {"archive_reason": "..."}.

POST /v1/templates/{identifier}/unarchive

Restore an archived template.

DELETE /v1/templates/{identifier}

Permanently erase a template and all its versions. Requires the template to be archived or to have no published versions. Forks keep their content; their parent pointer is set to null.

POST /v1/templates/{identifier}/transfer-ownership

Transfer ownership to another user. Returns an invitation; the recipient must accept.

Verifiers

POST /v1/verifiers

Deploy a semantic verifier (create or new version).

POST /v1/verifiers
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{
  "name": "response-clarity",
  "criterion": "The response is clear, direct, and free of unnecessary qualifiers.",
  "input_fields": ["user_query", "agent_response"],
  "examples": [
    {
      "inputs": {"user_query": "What is 2+2?", "agent_response": "4"},
      "expected": true,
      "reasoning": "Direct and complete."
    }
  ]
}
{
  "verifier_id": "vrf_...",
  "name": "response-clarity",
  "version": 1,
  "version_token": "...",
  "status": "active"
}

Subsequent deploys must include expected_version_token (the token from the last deploy, list, or get). Token mismatch returns 409 conflict.

GET /v1/verifiers

List your active verifiers. Query params: limit, cursor.

GET /v1/verifiers/{verifier_id}

Get a verifier version including criterion, calibration examples, and contract. Pass ?version=N to pin. Accepts UUID or accessible user-scope verifier name.

POST /v1/verifiers/{verifier_id}/runs

Execute a verifier judgment.

POST /v1/verifiers/vrf_.../runs
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{
  "inputs": {
    "user_query": "What is 2+2?",
    "agent_response": "It depends on your perspective."
  }
}
{
  "verifier_run_id": "vrn_...",
  "verifier_id": "vrf_...",
  "version": 1,
  "status": "complete",
  "passed": false,
  "reasoning": "The response hedges a straightforward factual question.",
  "duration_ms": 342,
  "created_at": "2024-06-01T12:00:00Z"
}

Use system:<name> as the path segment to invoke a platform-managed verifier: POST /v1/verifiers/system:workflow-design-qa/runs. Anonymous callers may run a verifier UUID if it appears in a live public template snapshot.

DELETE /v1/verifiers/{verifier_id}

Revoke a verifier (deactivate, keep run history).

DELETE /v1/verifiers/{verifier_id}/permanent

Permanently erase a verifier and all its data. Refused if a live published template snapshot references it; unpublish the template version first.

Image generators

POST /v1/image-generators

Deploy an image generator.

GET /v1/image-generators

List your active image generators. Query params: limit, cursor.

GET /v1/image-generators/{generator_id}

Get an image generator version.

POST /v1/image-generators/{generator_id}/runs

Generate images.

POST /v1/image-generators/{generator_id}/runs
Authorization: Bearer good_live_EXAMPLE_xxxxxxxx
Content-Type: application/json

{
  "prompt": "A calm mountain lake at dawn, photorealistic",
  "num_images": 1
}

generator_id can be a UUID, uuid@version to pin a version, or system:<tier> for a platform quality tier. The model body field lets authenticated callers run a one-off generation without a deployed generator (use any placeholder path segment such as -).

DELETE /v1/image-generators/{generator_id}

Revoke an image generator.

DELETE /v1/image-generators/{generator_id}/permanent

Permanently erase an image generator and all its run records.

Teams

All team endpoints require authentication.

POST   /v1/teams                                   # create a team
GET    /v1/teams                                   # list teams (filter: mine|member|all)
DELETE /v1/teams/{team_id}                         # delete a team
GET    /v1/teams/{team_id}/members                 # list members
POST   /v1/teams/{team_id}/members                 # add a member (returns invitation)
DELETE /v1/teams/{team_id}/members/{user_id}       # remove a member
POST   /v1/teams/{team_id}/transfer-ownership      # transfer ownership (returns invitation)

Create body: {"handle": "my-team"}.

Add member body: {"user_identifier": "email@example.com"}.

Invitations

Transfers of workflow or template ownership, team membership additions, and team ownership transfers all go through invitations. The recipient must accept for the action to take effect.

GET    /v1/invitations                         # list (filter: received|sent|all; state: pending|all)
POST   /v1/invitations/{id}/accept             # accept
POST   /v1/invitations/{id}/decline            # decline
POST   /v1/invitations/{id}/cancel             # cancel (proposer only)

Design

GET /v1/design/workflow-prompt

Returns the workflow designer prompt pack (the same payload the MCP design_workflow tool returns). Auth required.

Health and observability

GET /healthz

Returns {"status": "ok"}. Available on every host without auth.

See also