Skip to content

API reference

The base URL is the host (http://127.0.0.1:8002 by default); every API route sits under the API major /v1. Every response carries an X-Request-ID header for log correlation. Callers identify themselves with their own caller key in an X-API-Key: <value> header. Routes marked auth require a known key; the binding and sync-job routes are in a warning phase. See Authentication below.

Versions

The API has one supported major, v1. Every API route sits under it (/v1/...); the base URL is the host alone. /health, /docs, /redoc and /openapi.json are unversioned. The API major policy (supported majors, retirement window, what counts as a breaking change) is written once in Service API versions.

The same paths without the /v1 prefix (for example /bindings) remain as deprecated aliases: the same operations, deprecated on 2026-11-06 and removed no earlier than 2027-02-04. Every response on an alias carries three headers:

Deprecation: @1793923200
Sunset: Thu, 04 Feb 2027 00:00:00 GMT
Link: <https://blue-sand-0fe957b03.7.azurestaticapps.net/docs/review-sync-service/04-api-reference/>; rel="deprecation"

Deprecation is the deprecation instant as a Unix timestamp (RFC 9745), Sunset is the earliest removal date (RFC 8594), and Link points at this API reference. Responses under /v1 carry none of them.

/docs shows both mounts, with the aliases marked deprecated. The request log line carries api_version (v1, unversioned, or none for paths such as /health) and user_agent, so traffic still on the aliases is visible before they are removed.

Base URLs and interactive docs

Environment Base URL
Local http://127.0.0.1:8002
Non-prod deployed https://review-sync-service.bravecliff-5469885d.swedencentral.azurecontainerapps.io

The deployed service sits behind the VPN. Both environments serve the generated OpenAPI documentation, which is always in step with the running build in a way this page cannot promise to be:

Path What it is
/docs Swagger UI, with a request form for every route
/redoc The same schema as a reference page
/openapi.json The raw schema, for generating a client
Method Path Auth Purpose
GET /health no Liveness check
GET /v1/bindings warning phase List all registered review-sync bindings
GET /v1/bindings/{binding_id} warning phase Fetch one binding
PUT /v1/bindings/{binding_id} warning phase Create or replace one binding
POST /v1/bindings/validate warning phase Validate a binding definition without storing it
POST /v1/sync-jobs warning phase Run one sync job immediately for a stored binding
GET /v1/sync-jobs/{job_id} warning phase Load one stored sync-job result
POST /v1/langfuse-profiles yes Register or replace a Langfuse connection profile
GET /v1/langfuse-profiles yes List every Langfuse profile, without their secret keys
GET /v1/langfuse-profiles/{profile_id} yes Fetch one Langfuse profile, without its secret key
DELETE /v1/langfuse-profiles/{profile_id} yes Remove a Langfuse connection profile

Every /v1/langfuse-profiles route is auth-protected. Every /v1/bindings route (including POST /v1/bindings/validate) and the sync-job routes are in a warning phase, described under Authentication. ReviewSyncServiceClient sends an X-API-Key header whenever it is given an api_key; eval-review passes REVIEW_SYNC_API_KEY from the environment.

!!! note "POST /v1/sync-jobs is synchronous, unlike the scoring service's POST /v1/score-jobs" Both services expose a -jobs noun and they do not behave the same way. POST /v1/sync-jobs here runs the job and returns its terminal JobResult on the same request. The scoring service's POST /v1/score-jobs returns 202 with a job id you then poll. Sync jobs are short and their result is what the caller wants; scoring jobs wait on LLM judges and can outlast an HTTP timeout.

GET /health

Returns {"status": "ok"}. Used for local and cloud liveness checks.

GET /v1/bindings

Returns a BindingListResponse: every stored binding, sorted by binding_id.

GET /v1/bindings/{binding_id}

Returns one QueueToDatasetBinding, or HTTP 404 if binding_id is not registered.

PUT /v1/bindings/{binding_id}

Creates the binding if binding_id is new, or fully replaces it if it already exists. The path segment must match binding.binding_id in the request body (HTTP 400 otherwise). The binding also runs through the same semantic validation as POST /v1/bindings/validate before it is stored — a binding that fails validation is rejected with HTTP 400 and never written. Returns the stored binding. See 01-bindings.md.

POST /v1/bindings/validate

Runs the same semantic validation PUT /v1/bindings/{binding_id} runs, without storing anything. Returns a BindingValidationResult: valid plus a list of human-readable errors. See 01-bindings.md — Binding validation.

POST /v1/sync-jobs

Accepts a JobRequest (binding_id, dry_run, max_items, page_size, persist_artifacts) and runs one sync job immediately. Returns a JobResult (HTTP 404 if binding_id is not registered). See 02-sync-execution.md for the full execution flow and outcome semantics.

GET /v1/sync-jobs/{job_id}

Returns one previously stored JobResult, or HTTP 404 if job_id was never persisted.

POST /v1/langfuse-profiles

Registers or replaces a LangfuseConnectionProfile (registering a profile_id that already exists overwrites it). Returns the stored profile without its secret_key. See 03-langfuse-profiles-and-cleanup.md.

GET /v1/langfuse-profiles

Returns every registered profile as a list, each carrying profile_id, base_url and public_key. secret_key is never returned, by any route: it belongs to the pod that registered it, and the registry accepts a Key Vault URI precisely so the value need not be handled in the clear.

GET /v1/langfuse-profiles/{profile_id}

Returns one profile in the same redacted shape, or HTTP 404 if profile_id is not registered.

DELETE /v1/langfuse-profiles/{profile_id}

Removes a profile. Returns {"deleted": "<profile_id>"}, or HTTP 404 if profile_id was never registered. Deleting a profile that a binding still references does not fail the delete — the next sync job for that binding fails at credential resolution instead.

Authentication

Every caller has its own caller key, sent as an X-API-Key: <value> header.

Route Who
GET /health Open
The four /v1/langfuse-profiles routes Any known caller
Every /v1/bindings route, POST /v1/sync-jobs, GET /v1/sync-jobs/{job_id} Warning phase: a known caller is identified, and a call without a known key is still accepted and logged. A key becomes required from an enforcement date announced in the changelog

A request without a key, or with a key no caller has, to a protected route gets HTTP 401 Invalid or missing API key. What a profile points at is a pod's own Langfuse project, so reading it is as privileged as changing it. Every request log line carries caller=<id> (unknown for an unidentified call on a warning-phase route, - when none applies).

When CALLER_KEY_VAULT_URL is unset every route is open and no caller is identified, which is the local-development and test default. The service logs a warning at startup when that happens, so an unprotected deployment says so in its own logs rather than looking healthy. With it set, the service logs Loaded N callers: <ids> at startup.