THINKHX

Your first API call

The thinkhx API is a JSON-over-HTTPS API. You authenticate with a project API key, call an endpoint under /v1/, and read the result from a single response envelope. Everything a key can reach belongs to the one project the key was created in. Create a key from the workspace first, as described in Project API keys, then come back here.

Base URL

Every request goes to the API host of this project:

curl
https://api.thinkhx.com

Only HTTPS is served. A request on http:// is answered with a 301 redirect to the same path on https://, and every HTTPS response carries a Strict-Transport-Security header.

Important — Your key travels in a request header, so a single plaintext attempt discloses it in full. Always call the https:// URL directly rather than relying on the redirect.

There is one anonymous endpoint you can use to check the host is reachable:

curl
curl https://api.thinkhx.com/health
json
{
  "code": 200,
  "title": "OK",
  "response": "api gateway"
}

Authenticate

Send your key as a Bearer token on every call:

HeaderValue
AuthorizationBearer sk-project-…
Content-Typeapplication/json (only when you send a body)

The key is the project scoping: it is tied to one consumer and one project, and every query the API runs is filtered by that pair. A key can never read or write another project's data, whatever you put in the URL.

Only the SHA-256 hash of your key is stored, so a lost key cannot be recovered — delete it and create a new one. A key also has a status and an optional expiry: a disabled key and an expired key are both refused.

Make the call

This reads the project the key belongs to:

curl
curl https://api.thinkhx.com/v1/project \
  -H "Authorization: Bearer sk-project-YOUR_KEY"
json
{
  "code": 200,
  "title": "OK",
  "response": {
    "id": "proj_3f6c1d90ab77e2415c08",
    "object": "project",
    "name": "Acme",
    "description": "Acme's reasoning workspace",
    "country": "us",
    "default_locale": "eng",
    "is_verified": true,
    "team_total": 4,
    "status": "activated",
    "created_at": 1751328000,
    "updated_at": 1754205600
  }
}

The response envelope

Every endpoint answers with the same three keys, on success and on failure alike. Read the HTTP status code, then read response.

FieldTypeDescription
codeintegerThe HTTP status code, repeated in the body.
titlestringThe short status label, such as OK or Not Found.
responseobject, array or stringThe result on success; an explanatory message on failure.

A single-object endpoint puts the object straight into response, with an object field naming its type. A collection endpoint wraps its rows:

json
{
  "code": 200,
  "title": "OK",
  "response": {
    "object": "list",
    "data": [],
    "total": 0
  }
}

total counts every row that matches, not the number returned in data, so use it to drive pagination.

NoteGET /v1/catalog/public is the one exception: it needs no key and answers {"ok": true, "data": [...]} instead of the envelope. See Catalog.

Two URL shapes

Endpoints come in two families, and the path tells you which one you are calling.

ShapeExampleUsed by
/v1/<resource>/v1/projectProject resources: consumer, event, feedback, file, key, project, team, usage.
/v1/<module>/<action>/v1/app/listModule resources: app, catalog, run, run-usage.

Both families take an identifier as a final path segment when the endpoint acts on one record: /v1/team/pjin_… and /v1/run/detail/runi_….

Every identifier is a prefixed opaque string — proj_ for a project, mapp_ for an app, runi_ for a run — and you should treat it as an opaque value rather than parse it.

Scopes

A key carries a list of scopes, and each endpoint requires one. The grammar has three forms:

ScopeGrants
*Every action on every resource.
<resource>:*Every action on that one resource, such as team:*.
<resource>:<action>One action on one resource, such as team:read.

Reading needs the :read action, and creating, editing or deleting needs :write. Each topic in this reference names the scope its endpoints require. Call GET /v1/key/current to see the scopes your key actually holds. A call that is short of its scope is refused with 403 and a message naming what was missing.

Pagination

Module collections accept two query parameters:

ParameterTypeDefaultDescription
limitinteger50Rows to return, from 1 to 100. A value outside that range falls back to the default, and anything above 100 is capped at 100.
offsetinteger0Rows to skip. A negative value is treated as 0.

Project collections take no parameters: they return the 100 most recent rows, with the real count in total.

Dates and empty values

Every timestamp is an integer number of seconds since the Unix epoch, in UTC. A timestamp that has not happened yet is null rather than 0, so "expires_at": null means the key never expires. A string field that was never filled in is an empty string rather than null.

Request bodies

Send POST and PATCH bodies as a JSON object with Content-Type: application/json. A body that is not valid JSON, or that is not a JSON object, is refused with 400 and the message Invalid JSON. A body over 200,000 bytes is refused with 413.

A PATCH only touches the fields you send. Sending no recognised field at all is an error rather than a silent success, so a 200 means your change was applied.

Need help? Contact Support.