Skip to content

Pushing Data via the REST API

Omnismith exposes a REST API so scripts, agents, and automated pipelines can create and update entities without touching the UI.

Generate a Personal Access Token in your account settings and pass it as a Bearer token in every request:

Authorization: Bearer omni_your_token_here
PATCH https://api.omnismith.io/v1/entities/{id}
Content-Type: application/json
Authorization: Bearer omni_your_token_here
{
"attributes": {
"hostname": "prod-web-01",
"status": "active"
}
}

The attributes object maps attribute UUIDs or human-readable slugs to values:

Value ShapeExampleDescription
Scalar"prod-web-01", 42, trueUpdates the attribute to the new scalar value
nullnullClears/unsets the attribute value
Backfill Object{"value": "active", "updated_at": "2026-02-23T10:00:00Z"}Records value with explicit historical timestamp
Increment Object{"op": "increment", "value": 1}Adds to the stored number (number attributes and metrics); negative subtracts, never-set counts as 0

Keys may be slugs or UUIDs, mixed freely, but each attribute may appear only once per request and must belong to the entity’s template. Numbers and booleans are accepted as JSON values and serialized for you.

Use the increment object for counters — attendee counts, retries, stock levels — instead of reading the number and writing it back: the increment is applied to the stored value, so two writers bumping the same counter at the same time both land. It is accepted on update, replace, batch writes and action values, not on create (there is nothing to increment yet), and cannot be combined with updated_at.

The optional updated_at timestamp in backfill objects lets you record a dimension value at a specific point in the past. This is useful when:

  • A remote system buffered state changes during network downtime
  • You are migrating historical audit data from another system
  • You need to record an observation at an exact historical timestamp

When updated_at is supplied, Omnismith records the value at that timestamp in the entity audit trail. The History tab will reflect the correct timeline.

updated_at must be an RFC 3339 timestamp with an explicit offset — 2026-02-23T10:00:00Z or 2026-02-23T12:00:00+02:00. A timestamp without an offset (2026-02-23 10:00:00) is rejected with 422 rather than silently interpreted as UTC. The same rule applies to POST /v1/entities/{id}/metrics.

Terminal window
# Record historical dimension change with explicit timestamp
SERVER_ID="entity-uuid-here"
TOKEN="omni_your_token_here"
curl -s -X PATCH "https://api.omnismith.io/v1/entities/$SERVER_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"status": {
"value": "maintenance",
"updated_at": "2026-02-23T10:00:00Z"
}
}
}'

PUT /v1/entities/{id} makes the entity look exactly like the map you send: every non-metric attribute of the template that is absent from the map is cleared (and the clear is recorded in history). An explicit "attributes": {} clears them all. Metric attributes cannot be replaced — send them with PATCH or the metrics endpoint. Prefer PATCH unless you really mean “overwrite the whole record”.

POST /v1/entities/batch applies up to 100 mixed operations in order. Every operation carries exactly the fields for its op — id is always the entity id, template always the template slug or UUID:

{
"operations": [
{ "op": "create", "template": "server_node", "attributes": { "hostname": "edge-fra-01" } },
{ "op": "update", "id": "018b2f1b-8c1a-75b3-8000-7f0000010000", "attributes": { "status": "maintenance" } },
{ "op": "replace", "id": "018b2f1b-8c1a-75b3-8000-7f0000010001", "attributes": { "hostname": "edge-fra-02" } },
{ "op": "delete", "id": "018b2f1b-8c1a-75b3-8000-7f0000010002" }
]
}

replace behaves like PUT: attributes you leave out are cleared. Pass "atomic": true to run the whole batch in one transaction.

Two more operations identify records by your own ids: create accepts an optional external_key, and upsert (template, external_key, attributes) creates or updates the record holding the key — see below.

If the system you integrate has its own ids for the records — customer ids, hostnames, order numbers — store them as each record’s External Key and write with upsert instead of searching first and then creating:

PUT https://api.omnismith.io/v1/entities/template/server/by-key
Content-Type: application/json
Authorization: Bearer omni_your_token_here
{
"external_key": "api-prod-01",
"attributes": {
"ip_address": "10.0.10.21",
"cpu_usage": 41.2
}
}

The first call creates the record with the key (201, {"id": "...", "created": true}); every later call with the same key updates that record (200, "created": false) as a partial update. Metric values are appended on either branch. Sending the same key twice — a retry, a duplicated event, two workers racing — never creates two records. Read a record back with GET /v1/entities/template/{template}/by-key?key=api-prod-01.

UseWhen
Access token + REST APIYour own code or agent writes to Omnismith: it can hold a token, choose the request shape, and call several endpoints (search, upsert, metrics, actions).
Inbound EndpointA third-party system posts its own fixed payload and signs it its own way — Stripe, GitHub, Shopify, form tools, Zapier, n8n, or a device that should not hold an API token. The endpoint verifies the signature, maps the payload to attributes and upserts by external key, with a delivery log and replay.

An inbound endpoint only writes into its own template, so a leaked endpoint secret exposes far less than a leaked access token.

POST /v1/entities/aggregate/{template} answers “how many”, “how much” and “broken down by” questions in one call, computed server-side. It takes the same filter_groups as search, up to three group_by attributes (list, reference, string, number, boolean or date), and one to ten aggregations:

{
"filter_groups": [[{ "field": "status", "operator": "eq", "value": "018b2f1b-8c1a-75b3-8000-7f0000010020" }]],
"group_by": ["tier"],
"aggregations": [{ "op": "count" }, { "op": "sum", "field": "mrr" }]
}
{
"data": [
{
"key": [{ "field": "tier", "value": "018b2f1b-8c1a-75b3-8000-7f0000010031", "custom_value": "Team" }],
"aggregates": [{ "op": "count", "field": null, "value": 12 }, { "op": "sum", "field": "mrr", "value": 3400.5 }]
}
],
"limit": 50,
"truncated": false
}

count takes no field; sum and avg need a number attribute; min and max accept number, date and datetime attributes. Each group’s key and aggregates mirror the request order; custom_value carries the list label or the referenced record’s display value, and a null key groups the records that have no value. Groups come back ordered by the first aggregation descending, capped by limit (1-100, default 50) with truncated: true when more exist. Metric attributes are time series and are reduced with the chart endpoint instead.

StatusMeaningWhat to do
400The payload shape is wrong: missing attributes, a list instead of an object, an unknown field, a wrong type.Fix the request structure; detail says what was wrong.
422The shape is fine but the content is not: unknown attribute, attribute not on the template, the same attribute twice, a bad updated_at, or a value that fails its attribute type.Read errors — it names the exact field, e.g. attributes.status — and fix that entry.
404No such entity or template.Check the id or slug.
409The external_key is already held by another live record of the template, or a concurrent upsert took it.Pick another key, or send the upsert again.