Create records through the API
Create a record for an object through the Henceforth data API and confirm it was written.
Prerequisites
Section titled “Prerequisites”- The target object and its fields are already applied to the database. See From metadata to database.
- You know the object’s module key and object key (for example,
coreandAccount).
Routes
Section titled “Routes”All data API routes are derived from the module key and object key:
| Method | Path | Operation |
|---|---|---|
POST |
/v1/data/{moduleKey}/{objectKey} |
Create a record |
GET |
/v1/data/{moduleKey}/{objectKey}/{id} |
Read a record by ID |
PATCH |
/v1/data/{moduleKey}/{objectKey}/{id} |
Update a record (partial) |
DELETE |
/v1/data/{moduleKey}/{objectKey}/{id} |
Delete a record |
PUT |
/v1/data/{moduleKey}/{objectKey}/{fieldKey} |
Upsert on a unique field |
POST |
/v1/data/query |
Execute a HOQS query |
1. Send the create request
Section titled “1. Send the create request”POST /v1/data/core/Account
Request body — every field you set must be defined on the object, and system fields (id, createdAt, updatedAt, objectType) must be omitted:
{ "name": "Acme Corp", "industry": "Technology", "active": true}2. Read the response
Section titled “2. Read the response”A successful create returns 201 Created with the full record, including the system fields the runtime generated:
{ "id": "019502a4-b4e7-7f3a-8c1e-0a1b2c3d4e5f", "objectType": "core.Account", "createdAt": "2024-03-15T10:30:00.000Z", "updatedAt": "2024-03-15T10:30:00.000Z", "name": "Acme Corp", "industry": "Technology", "active": true}The response also includes a Location header with the URL of the created record.
3. For inherited objects, expect writes across the inheritance chain
Section titled “3. For inherited objects, expect writes across the inheritance chain”If the object extends another object, the runtime inserts into every table in the chain, base to leaf, sharing one UUID. Creating an Individual record inserts into hf.object, then core.party, then core.individual. Abstract objects cannot be created directly — the API returns ABSTRACT_OBJECT (422) if you try. See Objects, records, and fields for the non-inherited case and why the record is read back through the view before it’s returned.
Verification
Section titled “Verification”GET the Location header URL (or GET /v1/data/core/Account/{id} using the id from the response) and confirm the record comes back with the values you sent. See Read, update, and delete records through the API.
Validation
Section titled “Validation”On every create and update, the runtime validates the request body against the compiled metadata:
- Unknown fields are rejected –
UNKNOWN_FIELD(422) for any field name not defined in the metadata. - System fields (
id,createdAt,updatedAt) are read-only and rejected if present. - Formula fields are read-only and rejected if present.
- Rollup fields are read-only and rejected if present.
- Type checking – each field value must match the expected JSON type for its scalar type (strings for
text, booleans forbool, integers forint, and so on). - Required fields must be present and non-null on create. On update, required fields cannot be set to null.
- Lookup values must be valid UUID strings referencing a record that exists in
hf.object. For multi-target lookups, the referenced record’sobject_typemust be in the allowed set. - Abstract objects cannot be instantiated.
- Custom rules – object-level
rules:block expressions (declared in*_object.json5) are evaluated against the record. A rule that fails produces aRULE_FAILEDfield error, attributed to a pinned field, a single referenced field, or the whole record when the expression spans more than one field.
Error responses
Section titled “Error responses”Every error response is an array of entries, each naming the subsystem
(domain) and stable code that produced it:
{ "errors": [ { "domain": "data", "scope": "field", "target": "email", "code": "REQUIRED", "message": "Field 'email' is required" } ]}A request that fails several checks at once (e.g. two missing required fields) carries one entry per failure in the same array.
For the full list of codes this API can return, their HTTP statuses, and what each one means, see the generated error code reference – it is derived directly from the server’s own error types, so it never drifts from what the server actually sends.
See also
Section titled “See also”- Objects, records, and fields – what an object, record, and field are, how a create request becomes rows in the database, and why creates and updates validate against metadata
- Read, update, and delete records through the API
- Upsert records through the API
- FieldDefinition – full field type and validation reference