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"
}
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.