Hane Platform
Business data
Defining types and fields, value states and sources, reading and writing records with optimistic locking, filtering, relations, and CSV imports.
This page explains the concepts and use of business data in Hane (the Data service). You define types in manifests and read and write records through the public API (/v1/data).
Define types
Section titled “Define types”Business data types are defined with two manifests, Schema and Ontology. A Schema sets the fields and the shape of their values; an Ontology sets what types mean and how they relate. Every type that has a Schema must appear in some Ontology.
apiVersion: data/v1alpha1kind: Schemametadata: name: lease-contractspec: type: LeaseContract key: contract_no # business key, unique within the type display: contract_no # field shown as the name in lists fields: contract_no: { type: string, title: Contract number } start_date: { type: string, format: date, title: Start date } term_months: { type: integer, minimum: 1, maximum: 120, title: Term (months) } end_date: { type: string, format: date, title: End date, computed: lease-end-date } monthly_fee: { $ref: provider:Money, title: Monthly fee (excl. tax), classification: confidential } status: { enum: [active, notice_sent, renewed, replaced, returned, terminated], title: Status } required: [contract_no, status] index: [end_date, status]| Field option | Meaning |
|---|---|
type |
string, integer, number, boolean |
format |
date (YYYY-MM-DD), date-time, email |
enum |
The list of allowed values |
$ref: provider:Money |
A monetary amount. The value has amount_minor (an integer in the currency’s minor unit; yen for JPY) and currency |
computed |
The name of a rule (Rule). The value is calculated by the rule in CEL and cannot be written through the API |
classification |
Confidentiality class (public, internal, confidential, restricted) |
Relations between types go in the Ontology. A relation with temporal: valid-time has a validity period, so you can trace which relation was in effect on a given day.
apiVersion: data/v1alpha1kind: Ontologymetadata: name: vehicle-leasespec: types: LeaseContract: { label: Lease contract, aliases: [contract, lease], schema: lease-contract } Vehicle: { label: Vehicle, aliases: [company car], schema: vehicle } relations: covers: { from: LeaseContract, to: Vehicle, label: Covered vehicle, cardinality: many-to-one, temporal: valid-time }To change a type, change the manifest and activate a new ReleaseBundle. To rename a field or change its type, write a Migration. See Managing definitions as code for details.
Value states and sources
Section titled “Value states and sources”Every value in Hane’s business data records how far it has been verified and where it came from. This separates values read by AI from values verified by people.
| Value state | Meaning |
|---|---|
confirmed |
Backed by a document or by a person’s check |
estimated |
Has some basis but has not been verified. Calculated values are also estimated |
pending_inquiry |
Could not be read from documents and is being asked of the other party |
unknown |
Not obtained and not being inquired about |
- A value written without a state is estimated. A
nullvalue is unknown. - Only a person with the
data:values.confirmscope can make a value confirmed. AI and service accounts cannot. - The state of a calculated value is the weakest state among its inputs.
- The source (
source) records whether a value came from an import, a person, a calculation, a document, an external system, or a workflow, with details such as the document page and quote.
Read and write records
Section titled “Read and write records”To create a record, send the type and a value for each field.
POST /v1/data/recordsAuthorization: Bearer <API key>Idempotency-Key: 5c2a…Content-Type: application/json
{ "type": "LeaseContract", "values": { "contract_no": { "value": "C-1001" }, "status": { "value": "active" }, "start_date": { "value": "2024-04-01", "state": "estimated", "note": "From the first registration date on the vehicle certificate" }, "monthly_fee": { "value": { "amount_minor": 52000, "currency": "JPY" } } }}The response is the record, with the record’s version in the ETag header. If a record of the same type with the same key already exists, the response is 409.
| Operation | API |
|---|---|
| Get (with relations and change history) | GET /v1/data/records/{id} |
| Update | PATCH /v1/data/records/{id} with If-Match and an optional reason |
| History of a field’s values | GET /v1/data/records/{id}/values/{field}/history |
| Confirm a value | POST /v1/data/records/{id}/values/{field}/confirm |
| Active types and fields | GET /v1/data/types |
Optimistic locking
Section titled “Optimistic locking”A record has an integer version that increases with every update. To update, send the ETag you received in If-Match.
PATCH /v1/data/records/ent_01J…Authorization: Bearer <API key>If-Match: "4"Content-Type: application/json
{ "values": { "status": { "value": "returned" } }, "reason": "The vehicle was returned" }Without If-Match the response is 428 PRECONDITION_REQUIRED; if someone else updated first, it is 412 PRECONDITION_FAILED. On 412, read the record again, reapply your change, and send it again. In the SDK, ifMatch(version) builds the header value.
Listing and filtering
Section titled “Listing and filtering”GET /v1/data/records lists the records of one type.
| Parameter | Meaning |
|---|---|
type |
The type (required) |
where |
A CEL condition. Variables are record and states (for example, record.status == "active") |
state, field |
Only records whose field has the value state state |
include=relations |
Include each record’s current relations |
limit, cursor |
Page size (up to 500) and the next_cursor from the previous page |
GET /v1/data/records?type=LeaseContract&where=record.status%20%3D%3D%20%22active%22&state=estimated&field=end_dateAuthorization: Bearer <API key>To read by following relations, use a typed query (POST /v1/data/ontology/query). It checks types and relations against the definitions and then follows them a fixed number of hops (up to 3). Free-form SQL and graph exploration are not accepted.
Relations
Section titled “Relations”Create a relation with POST /v1/data/relations, specifying the relation type and the source and target records.
- For a many-to-one relation with a validity period, creating a new relation closes the current one at
valid_from(today if omitted) and starts the new one from that day. - A written relation starts as
inferred. UsePOST /v1/data/relations/{id}/stateto make itconfirmedor to record a doubt (disputed). Confirming requires a person withdata:values.confirm.
CSV imports
Section titled “CSV imports”You can import existing ledgers as CSV. Send the type and the CSV text to POST /v1/data/imports. Headers are field names; a <field>@state column sets the value state, and an @rel:<relation> column gives the key of the related record. Values without a state get default_state (estimated if omitted). With hanectl, use hanectl import <Type> -f <file>.