Skip to main content
The Data API reads and writes the records of an app’s entities. The JS SDK is a thin client over it; anything the SDK does, you can do over HTTP. Every endpoint is listed in the Data API reference, generated from the platform’s routes.

Base URL

{org} is your organization’s runtime subdomain and {project} the project’s slug. Records live under /entities/{entity}/records, where {entity} is the entity’s slug.

Authentication

Full details, including how each credential is scoped: Authentication → Data API.

Creating a project API key

Open the project in the HASP platform and choose API keys in the project’s sidebar. A project admin selects New key, names it, and picks Live or Test; the key’s prefix (hasp_pak_live_ / hasp_pak_test_) records that choice. The full key is shown once, right after it is created — copy it then, because HASP stores only a hash of it and cannot show it again. Every project member can see the key list (name, environment, last four characters, created and last-used dates, status); only project admins can create, rotate, or revoke keys.
  • Rotate issues a replacement key immediately. The old key keeps working for 72 hours so an integration can switch over without downtime, then it is revoked automatically.
  • Revoke takes effect immediately: the next request with that key fails with API_KEY_REVOKED.
A key reaches only the project it was created in. Issuing, rotating, and revoking a key are each recorded in the audit log.

App-level endpoints

Two endpoints sit beside the project-scoped ones, at https://{org}.usehasp.run/api/hasp/v1, and are addressed by the X-Hasp-AppId header rather than a project:
  • GET /bootstrap — the signed-in user, their org and app role, the app, and its Data API limits. Called with the runtime session; a public app also answers an anonymous caller. The SDK’s getBootstrap().
  • GET /status — liveness, callable without credentials. With a project API key it also reports whether an app of the key’s project is in maintenance. The SDK’s getStatus().

Records

A record is the entity’s declared fields plus three system fields:
The fields, their types, and their constraints come from the entity’s schema, returned by GET /projects/{project}/schema. The published reference describes records as EntityRecord: the system fields plus additionalProperties, with an x-hasp-entity-schema extension pointing at that schema endpoint — the API spec cannot know your fields, the schema endpoint does. Writes send field values under data. Keys that are not declared fields are dropped; values are validated against each field’s type and constraints, and a failing field is reported in error.details under its key. Numbers are stored exactly. A number or currency value that cannot be held exactly — an integer beyond 64 bits, or a decimal with more precision than a double carries — is rejected with 422 rather than rounded. Send large identifiers as text. Bulk endpoints (/records/bulk) take up to 100 items and are all-or-nothing: every item is written in one transaction, or none is. A failing item is reported at its position — records.3.title, updates.0.id, ids.2.

Filtering, sorting, and pagination

  • filter — a JSON object of conditions. See Filtering for the syntax and the operators each field type supports.
  • sort — field or field:asc|desc, on created_at, updated_at, or any declared non-json field.
  • cursor / pageSize — keyset pagination. See Pagination.
A filter, sort, or cursor that cannot be applied exactly as written is rejected with 422, never ignored.

Indexing fields

Set indexed: true on a field (the indexed property on the AI API schema field endpoints, POST /v1/projects/{project}/schemas/{entity}/fields and PATCH …/fields/{field}, or indexed: true on a field in Studio’s entity contract) and HASP builds a database index on it. Filters with eq, gt, gte, lt, lte, and in, and sorts on the field, are then served from the index instead of scanning the entity’s records. neq, contains, and starts_with scan regardless.
  • Indexable types: number, currency, date, boolean, enum, reference, email, and phone — types whose values are bounded. text, multiline, and json cannot be indexed; the schema API rejects indexed: true on them with 422.
  • At most 10 indexed fields per entity. Every index is maintained on every write, so index the fields you filter or sort on routinely — identifiers, statuses, dates — not every field.
  • At most 50 indexed fields per organization, across all its projects.
  • String values of an indexed field are limited to 255 characters, so they always fit in the index. Turning indexed on for a field that already holds a longer value is rejected with 422; an indexed enum’s allowed values must fit too.
The index is built in the background after the schema change; until it exists, queries return the same results, just without the speed-up.

References

A reference field holds the id of a record of another entity, and must declare which entity (constraints.entity_slug). When a record is written, each reference is checked: the target must exist, belong to the declared entity in the same project, and live in the same environment — a production record can only reference production records, and a sandbox record only records in its own sandbox. References are checked when written, not maintained afterwards. If the referenced record is later deleted, the reference keeps its value; resolve it with a read and treat 404 as “no longer exists”. An update that resends a reference unchanged is not re-checked, so a record whose target was deleted can still be edited.

Errors

Every Data API error — on the app-level endpoints and for every credential type, agent credentials included — uses the same envelope as the AI API:
Validation failures are 422 VALIDATION_FAILED with every failing field in error.details, keyed by its path. The full code list is in Errors.

Endpoints

The endpoint reference in this section is generated from the platform’s routes into openapi/data-v1.json and checked for drift in CI.