Skip to content
HaneDOCS日本語

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).

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/v1alpha1
kind: Schema
metadata:
name: lease-contract
spec:
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/v1alpha1
kind: Ontology
metadata:
name: vehicle-lease
spec:
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.

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 null value is unknown.
  • Only a person with the data:values.confirm scope 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.

To create a record, send the type and a value for each field.

POST /v1/data/records
Authorization: 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

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.

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_date
Authorization: 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.

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. Use POST /v1/data/relations/{id}/state to make it confirmed or to record a doubt (disputed). Confirming requires a person with data:values.confirm.

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>.