rebase.energy

Objects and records

The object type catalog, the shape every record shares, and the generic endpoints that serve them.

Everything you store on the platform is a record of some object type. The object type says what kind of thing a record is and which attributes it has; the record is one concrete instance. Two types are built in, sites and assets, and a workspace adds its own. Whatever the type, records have the same shape and are served by the same handful of endpoints.

The catalog

GET /objects lists the object types in your workspace. Each entry's name is the slug you use in every other route.

r = requests.get(f"{BASE}/objects", headers=HEADERS)
r.raise_for_status()
for t in r.json()["items"]:
    print(t["kind"], t["name"], [(a["slug"], a["type"]) for a in t["attributes"]])
standard sites []
standard assets []
custom meters [('ean', 'text'), ('voltage', 'select'), ('zone', 'object')]
custom zones [('country', 'text'), ('area', 'geometry')]
FieldMeaning
nameURL slug, used as {object_name} below. Immutable.
singular, pluralDisplay labels.
kindstandard for the built-ins, custom for types you define.
attributesThe type's attribute schema: value attributes, the ones that hold records, and a between attribute if its records sit between two others. Empty for standard types, whose fields are fixed.
description, iconOptional presentation.

See Standard objects for what the built-in types hold and Custom object types for defining your own.

The record shape

Every record is one envelope plus values. The envelope is the platform's and is identical for every type; values is the type's schema and nothing else. Because the two never share a namespace, an attribute can carry any slug, even id or parent_id.

Envelope fieldMeaning
idGenerated UUID.
objectThe type slug, for example sites.
nameThe record's name.
created_at, updated_atTimestamps, UTC.
parent_id, parent_nameSet on a part: the record that owns it. null otherwise.
statsNumbers the platform computes, such as a site's total_capacity_kw.
valuesThe attributes, keyed by slug.

Inside values, a value attribute is stored as given. An attribute that holds records comes back resolved: a reference as {"id", "name", "object"}, a reference list as a list of those, parts inlined as whole records one level deep, and a between attribute as the two-item list of its ends. Lists are capped in the record itself; the attribute's own route pages the whole thing.

Writes use the same two halves: {"name": ..., "values": {...}}.

The generic endpoints

Every object type, built in or custom, is served by these routes:

RoutePurpose
GET /objects/{object_name}List records.
POST /objects/{object_name}Create a record. The body is validated against that type's schema.
GET /objects/{object_name}/{record_id}Fetch one record.
PATCH /objects/{object_name}/{record_id}Partial update.
DELETE /objects/{object_name}/{record_id}Delete.
GET /objects/{object_name}/statsTotals over the whole type.
GET /objects/{object_name}/exportStream every record of the type.

The shape of values differs per type; the mechanics do not. Learn them once and they apply everywhere.

List

r = requests.get(f"{BASE}/objects/sites", headers=HEADERS)
r.raise_for_status()
for site in r.json()["items"]:
    print(site["id"], site["name"], site["stats"]["total_capacity_kw"])

Lists return up to limit records (default 50, max 200) in items. When there are more, next_cursor is set; pass it back as ?cursor= to fetch the next page. ?fields= trims values to the attributes you name, ?filter[<attribute>]= narrows a custom type's list server-side, and ?parent_id= lists one owner's parts. See Bulk record reads for filters, totals, and exports in depth.

Create

r = requests.post(f"{BASE}/objects/sites", headers=HEADERS, json={
    "name": "Utsira",
    "values": {"latitude": 59.3068, "longitude": 4.8896},
})
r.raise_for_status()
site = r.json()

The response is the full record, including its generated id and timestamps. Every record needs a name (except a record that sits between two others, see Linking records); names may not contain / and must be unique among siblings. A conflict returns 409. A create that nests parts, such as a site with its assets, is all or nothing.

Fetch

r = requests.get(f"{BASE}/objects/sites/{site['id']}", headers=HEADERS)
r.raise_for_status()

A record in another workspace does not resolve and returns 404, exactly like a record that does not exist.

Update

PATCH applies only the fields present in the body, values included. Send the attributes you change.

r = requests.patch(f"{BASE}/objects/sites/{site['id']}", headers=HEADERS, json={
    "values": {"description": "Small island wind site"},
})
r.raise_for_status()

Part lists are additive on PATCH and reference lists are replaced; Conventions has the details.

Delete

requests.delete(f"{BASE}/objects/sites/{site['id']}", headers=HEADERS).raise_for_status()

Returns 204. Deleting a record deletes its parts (a site's assets) and every timeseries attached to it and to them. Records it merely references are kept, unless you pass ?delete_members=true on a type with reference attributes to delete those too.

How records relate

Records relate to each other in three ways, and the difference decides what a write or a delete takes with it.

  • Parts. An attribute with relation: part holds records that belong to this one alone: a site's assets, a plant's turbines. Parts are created inside their owner or with parent_id, read back inlined, and deleted with the owner.
  • References. An attribute with relation: reference points at records that live independently: a synchronous area's zones. The same record can be referenced from many places, and cutting a reference never deletes it.
  • Between. A type with a between attribute makes each of its records the relationship between two others: an interconnection between two zones. It carries its own attributes and timeseries.

Linking records covers all three, including the per-attribute routes for listing, linking, and unlinking.

Timeseries

Any record of any type can carry timeseries. That is a large enough topic to get its own guides: Upload / read timeseries for one record, Many series at once for a fleet.

On this page