API referenceTenvelo API
https://api.tenvelo.com The contract, as OpenAPI

Contract 2026-10-10. Every path below carries its own major version.

Merchant · Reference

Audit

Who changed what, and when.

Operations
1
Base
https://api.tenvelo.com
  1. GET/v1/changes

    List who changed what

    No. listChangeLogAuth audit:read

    The merchant's audit trail: every change to every resource, newest first, with who made it. Paged with the cursor of the previous page. Changes arrive at the head of this list, so a cursor rather than an offset keeps a later page from skipping the rows the first was pushed down by.

    The trail names every actor in the business, so only an owner or a manager may read it.

    Parameters

    • cursorquery · string

      Opaque cursor from a previous page's next_cursor. Omit to start from the beginning.

      Pass it back exactly as received. It is base64 and its structure is not part of this contract — decoding it, or constructing one by hand, will break without notice.

    • limitquery · integer · int32

      Maximum items to return. The server may return fewer, so always drive the loop from has_more rather than from the size of the page you got back.

    Responses

    • 200

      A page of changes.

      Change log page · application/json
      • has_moreboolean

        Required

        Whether another page may follow.

      • itemsarray of Change log entry

        Required

        • actor_namestring

          Required

          The member's or device's name, or an integration's subject. Empty for a change no principal made, such as a scheduled price taking effect.

        • location_idstring · uuid

          RequiredNullable

          The location the change belongs to, or null for one that spans the merchant.

        • occurred_atstring · date-time

          Required

        • opstring · enum

          Required

          createdupdateddeleted

        • resource_idstring · uuid

          Required

          The id of the resource that changed.

        • resource_typeResourceType

          Required

          The kind of resource a change or operation refers to.

          Used to filter the change feed so a till can pull only what it needs — a countertop device syncing the catalogue has no use for invite or payout changes.

          This list grows, and a client is expected to meet a value it predates. A device is deployed behind the server and cannot be force-upgraded, so the contract's instruction is to ignore a type you do not understand rather than fail the page — failing it would wedge the device's cursor and stop it receiving anything at all.

          Declared with x-extensible-enum rather than enum for exactly that reason. The cost is that generated clients type this as a string instead of an enumeration; the alternative is that every new resource type reads as a breaking change, which would be true only for a client that treated the list as closed — and this one says, in writing, that it is not.

          open_order is historic: a tab is an order in the open state now (docs/decisions/0012), and the value appears only on changes recorded before that. Nothing new carries it. So are tax, whose changes are tax_rate now, and modifier_option, whose are modifier.

          customer only ever arrives as a tombstone (op: deleted, no body): the guest was erased. The guest book itself is never on the feed, so there is nothing to remove by that id; forget whatever you cached about the guest under it. Their bookings and queue entries arrive as changes of their own, already scrubbed.

      • next_cursorstring

        Nullable

        Pass as cursor for the next page, when has_more is true.

    • 400

      The request could not be parsed. Malformed JSON, a bad query parameter, or a header that failed to decode, among them an API-Version later than the server speaks or not a date (api-version-unknown, with the server's own in api_version).

      Distinct from 422: a 400 never reached validation, so invalid_params is absent. Retrying without changing the request will fail again, except that a request refused for its API-Version goes once the server is updated.

    • 401

      No credentials, or credentials that could not be verified — missing bearer token, malformed JWT, expired token, or a signature that failed against the issuer's JWKS.

      Refresh the token and retry once. Repeated 401 after a refresh means the device needs to re-enrol.

    • 403

      Authenticated, but not permitted. The token is valid and its merchant was resolved, but the caller's role does not allow this operation, or the target belongs to a different merchant.

      Cross-tenant access is reported as 403 on a resource the caller may not see, and as 404 where even confirming existence would leak information. Retrying will not help.

    • 410

      The request names an API-Version this server no longer serves (docs/decisions/0011). Nothing in it was read. api_version names the version the server speaks: the client is behind, and sends the request again once it is updated.

    • 422

      The request parsed but failed validation.

      invalid_params lists every failing field with a JSON Pointer, not just the first, so a client can mark up a whole form in one pass rather than making the user fix errors one round trip at a time.

      Also returned when an Idempotency-Key is replayed with a different body, and when device-computed order totals disagree with the server's recomputation.

    • 429

      Rate limit exceeded. Honour Retry-After rather than applying your own backoff.

      A fleet of tills coming back online together after an outage is the realistic cause, which is exactly when uncoordinated client-side retry turns a brief outage into a sustained one. The limit is per process, and per site for what records trade there — a request spends its site's budget, and a till's own smaller one inside it — and per (client, merchant) for everything else; no RateLimit field is sent, so this response and its Retry-After are the whole of the contract.

    • 500

      Something failed on our side. trace_id identifies the request in our tracing — quote it in a support ticket.

      The body never carries internal detail: no SQL, no stack traces, no upstream error strings. Safe to retry with the same Idempotency-Key.