Skip to content

REST and GraphQL API

Everything the web application does goes through the same public API you can call yourself. There are two ways in — a REST API for automation and a GraphQL endpoint that the application itself uses — and both sit behind the same authentication and the same permission checks.

Base URL: https://api.ownlate.com

CredentialLooks likeUsed for
API keyown_…Server-to-server calls, CI, scripts
SDK tokensdk_…The browser SDK, one project only
OAuth access tokenopaqueThe MCP server and third-party apps
Session cookieThe web application in your browser

Pass a key as a bearer token:

Terminal window
curl "https://api.ownlate.com/v1/workspaces" \
-H "Authorization: Bearer $OWNLATE_API_KEY"

Create keys under Profile → API keys. A key is shown once, at creation, and only its prefix afterwards. Each key carries:

  • Scopes — the permission codes it may exercise, which can only narrow what you already hold, never widen it.
  • An optional expiry — after which the key stops working.

Revoking a key takes effect immediately. See Members and access for the permission codes themselves.

For every call, the server works out which workspace it concerns, then checks what the caller holds against it:

  • The workspace comes from the path. Query strings and request bodies are not considered when resolving it, so an endpoint whose path names no workspace refuses with Workspace ID is required rather than guessing.
  • An API key or OAuth token narrows further: a permission outside its scopes, or a workspace outside those it was issued for, is refused before the workspace is even asked.
  • An anonymous caller gets what a viewer would see — and no more.
  • An open project additionally accepts suggestions from anybody signed in.

Failures come back as JSON with the status repeated in the body:

{ "message": "Forbidden", "error": "Forbidden", "statusCode": 403 }
StatusMeans
400The request did not validate
401No credential, or one that is no longer good
403Authenticated, but not allowed to do this
404No such object, or no such route
409The object is not in a state that allows this — approving something already approved

Request bodies are limited to 1 MB. For files larger than that, split the upload by file.

The generated OpenAPI document is served at /swagger-json, and the application renders it at platform.ownlate.com/docs/swagger. It is generated from the running server, so it is never out of date with respect to what is deployed.

MethodPath
GET POST/v1/workspaces
GET PUT/v1/workspaces/{workspaceId}
GET/v1/workspaces/{workspaceId}/audit-log
POST/v1/workspaces/{workspaceId}/sync-word-usage
GET/v1/workspaces/{workspaceId}/users/me/permissions
GET POST/v1/workspaces/{workspaceId}/members
DELETE/v1/workspaces/{workspaceId}/members/{userId}
GET/v1/workspaces/users/lookup
GET PUT/v1/workspaces/levels/{level}
GET POST/v1/workspaces/api-keys
DELETE/v1/workspaces/api-keys/{keyId}
GET/v1/users/me
MethodPath
GET POST/v1/workspaces/{workspaceId}/projects
GET PUT DELETE/v1/workspaces/{workspaceId}/projects/{id}
PUT/v1/workspaces/{workspaceId}/projects/{id}/restore
DELETE/v1/workspaces/{workspaceId}/projects/{id}/permanent

DELETE on a project archives it; permanent destroys an archived one.

MethodPath
GET POST/v1/workspaces/{workspaceId}/translation-files
POST/v1/workspaces/{workspaceId}/translation-files/upload
PUT DELETE/v1/workspaces/{workspaceId}/translation-files/{id}
POST/v1/workspaces/{workspaceId}/translation-files/rename-folder
POST/v1/workspaces/{workspaceId}/translation-files/merge-duplicates
MethodPath
GET POST/v1/workspaces/{workspaceId}/segments
GET DELETE/v1/workspaces/{workspaceId}/segments/{id}
PUT/v1/workspaces/{workspaceId}/segments/{id}/translate
PUT/v1/workspaces/{workspaceId}/segments/{id}/draft
PUT/v1/workspaces/{workspaceId}/segments/{id}/source-text
PUT/v1/workspaces/{workspaceId}/segments/{id}/translations/{language}/review
PUT/v1/workspaces/{workspaceId}/segments/{id}/translations/{language}/approve
PUT/v1/workspaces/{workspaceId}/segments/{id}/translations/{language}/reject
POST/v1/workspaces/{workspaceId}/segments/{id}/auto-translate
GET/v1/workspaces/{workspaceId}/segments/{id}/qa
POST/v1/workspaces/{workspaceId}/segments/approve-all
POST/v1/workspaces/{workspaceId}/segments/reject-all
POST/v1/workspaces/{workspaceId}/segments/pre-translate
GET/v1/workspaces/{workspaceId}/segments/progress
GET/v1/workspaces/{workspaceId}/segments/analytics
GET/v1/workspaces/{workspaceId}/segments/by-keys
GET/v1/workspaces/{workspaceId}/segments/translation-memory
GET/v1/workspaces/{workspaceId}/segments/export
GET/v1/workspaces/{workspaceId}/segments/export-zip
MethodPath
GET POST/v1/workspaces/{workspaceId}/glossary
PUT DELETE/v1/workspaces/{workspaceId}/glossary/{id}
GET/v1/workspaces/{workspaceId}/glossary/matches
GET POST/v1/workspaces/{workspaceId}/comments
DELETE/v1/workspaces/{workspaceId}/comments/{id}
GET/v1/workspaces/{workspaceId}/translation-history/{segmentId}
MethodPath
GET POST/v1/workspaces/{workspaceId}/releases
DELETE/v1/workspaces/{workspaceId}/releases/{id}
GET/v1/workspaces/{workspaceId}/releases/distribution
POST/v1/workspaces/{workspaceId}/releases/distribution/regenerate
MethodPath
POST/v1/workspaces/{workspaceId}/integrations
GET/v1/workspaces/{workspaceId}/integrations/by-project/{projectId}
GET PUT DELETE/v1/workspaces/{workspaceId}/integrations/{id}
PATCH/v1/workspaces/{workspaceId}/integrations/{id}/activate
PATCH/v1/workspaces/{workspaceId}/integrations/{id}/pause
PATCH/v1/workspaces/{workspaceId}/integrations/{id}/sync
PATCH/v1/workspaces/{workspaceId}/integrations/{id}/test
POST/v1/workspaces/{workspaceId}/integrations/test-direct
POST/v1/workspaces/{workspaceId}/integrations/{id}/create-pr
MethodPath
GET/v1/plans
GET/v1/payment/providers
GET/v1/workspaces/{workspaceId}/subscription
PUT/v1/workspaces/{workspaceId}/subscription/plan
POST/v1/workspaces/{workspaceId}/subscription/checkout
POST/v1/workspaces/{workspaceId}/subscription/portal
MethodPath
GET POST/v1/languages
DELETE/v1/languages/{code}

These need no credential at all. They serve public projects and OTA distributions.

MethodPathReturns
GET/public/v1/projectsPublic projects of a workspace
GET/public/v1/projects/{id}One public project
GET/public/v1/segments/progressProgress of a public project
GET/public/v1/segments/translations-mapEvery translation of a public project, grouped by file and language
GET/public/v1/ota/{accessKey}/manifestLatest release version and languages
GET/public/v1/ota/{accessKey}/bundlesBundle for every language
GET/public/v1/ota/{accessKey}/bundles/{language}Bundle for one language
GET/public/v1/plansPlans with prices
GET/public/v1/workspaces/{workspaceId}Public workspace details

The SDK endpoints — /public/v1/sdk/segments and /public/v1/sdk/segments/{id}/translations/{lang} — need an SDK token rather than an API key. See SDK and clients.

POST /graphql carries the same operations the web application uses, which is a superset of what REST exposes: invites, tasks, suggestions, notifications, QA rules, SDK tokens, translation memory entries and the analytics queries.

Terminal window
curl -X POST https://api.ownlate.com/graphql \
-H "Authorization: Bearer $OWNLATE_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"query":"query($id:ID!){ project(id:$id){ name progress { language progress } } }","variables":{"id":"…"}}'

Two things to know before you point a client at it:

  • The content type matters. A request that looks like it could have come from an HTML form — application/x-www-form-urlencoded, multipart/form-data or text/plain — is rejected as a possible cross-site request unless it names an operation in the x-apollo-operation-name header. Send application/json and this never comes up.
  • Permission is resolved from arguments. Pass workspaceId where the schema asks for it, including inside input objects; a mutation that leaves it out cannot be authorised.