Utility Endpoints
The seven stateless utility endpoints under /api/json/utilities (hash, verify-hash, uuidv7-timestamp, ulid-timestamp, resolve-id, jcs-canonicalize, server-time) and their GraphQL query twins, with request and response shapes, the result envelope, auth and OAuth scopes, and resolve-id gating.
Overview
Alongside its resource endpoints, Truestamp exposes seven small, stateless utility
operations: computing a hash, verifying a hash, extracting the time embedded in a
UUIDv7 or a ULID, resolving what an opaque id refers to, canonicalizing a JSON
object, and reading the server’s current clock. Six are pure compute with no data
access; one (resolve-id) reads data and is gated by what the caller is allowed to
see. None of them creates or changes any
record. Each operation is available in two equivalent forms: as a POST route under
/api/json/utilities on the JSON:API surface, and as a query twin
on the GraphQL surface. The same operations are also reachable by
LLM agents through the MCP server. Use them to
reproduce Truestamp’s hashing locally, to sanity-check inputs before submitting an
item, or to identify an unknown identifier before fetching its full record.
Calling conventions
All seven operations require authentication: present an
API key or an OAuth 2.1 access token as an
Authorization: Bearer header, exactly as on the rest of the API. An anonymous
request is rejected with 401 Unauthorized.
For OAuth callers only the api:read scope is required, on both surfaces. The
GraphQL twins are queries, so api:read follows from the usual query rule. The
JSON:API forms are POST routes only because Ash mounts every generic action that
way; they write nothing, so they are on the REST surface’s explicit read-only route
list and require api:read rather than api:write. API-key callers are not
scope-limited on either surface. Requests count against the same per-caller rate
limits as the rest of each surface.
JSON:API form
Send the operation’s arguments as a JSON object under a top-level data key. The
response wraps the operation’s result under a top-level result key (these routes
use a result envelope rather than a JSON:API resource document):
curl -X POST https://www.truestamp.com/api/json/utilities/hash \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/vnd.api+json" \
-d '{"data": {"text": "hello"}}'
{
"result": {
"algorithm": "sha256",
"hex": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
"input_byte_length": 5
}
}
A successful call answers 201 Created, not 200 OK. Ash mounts every generic
action as a POST route and derives the status from the verb alone, so the
read-only utilities report a creation status even though they create nothing.
Treat any 2xx as success rather than matching on 200.
Argument keys are snake_case (hash_type, time_zone). The seven routes appear in
the OpenAPI specification at /api/json/open_api (and in the Swagger UI and ReDoc
pages) under the operation names hash, verifyHash, uuidv7Timestamp,
ulidTimestamp, resolveId, jcsCanonicalize, and serverTime.
GraphQL form
The twins are the queries hash, verifyHash, uuidv7Timestamp, ulidTimestamp,
resolveId, jcsCanonicalize, and serverTime, with camelCase arguments
(hashType, timeZone). Each answers with a { result, errors } envelope whose
result is the same result object serialized as a JSON string, which the client parses:
query {
hash(text: "hello") { result errors { code message } }
}
The returned result is the JSON string
{"algorithm":"sha256","hex":"2cf24...","input_byte_length":5}. errors is empty
unless the request itself was refused (a missing argument, a rate limit); a failure
the caller can fix still arrives inside result as the structured object below.
Expected failures are structured results
Bad input a caller can fix (an unsupported algorithm name, a malformed ULID, an unrecognized id) does not surface as an HTTP error. The operation succeeds and its result is a structured failure object:
{"ok": false, "code": "invalid", "message": "Not a valid ULID", "retryable": false}
A successful result never carries an ok key, so check for "ok": false before
using the result. A missing required argument, by contrast, is a normal request
error on each surface.
hash: compute a digest
POST /api/json/utilities/hash / GraphQL hash. Computes a hash of the given UTF-8
text and returns it as a lowercase hex string.
Arguments:
text(required): the text to hash.hash_type(optional, defaultsha256): one of the same twelve algorithms accepted for item claims:md5,sha1,sha224,sha256,sha384,sha512,sha3_224,sha3_256,sha3_384,sha3_512,blake2s,blake2b.domain(optional): a Truestamp domain-separation name. When set, the result is SHA-256 over a single registered prefix byte followed by the text, reproducing a Truestamp internal domain-separated hash (see the byte-prefix registry);hash_typeis ignored. One of:items_claims,items_metadata,item,entropy,entropy_metadata,observation,block,block_genesis,block_metadata,commitment_data,commitment,proof.
Returns hex, algorithm, and input_byte_length; in domain mode also domain and
prefix (the prefix byte in 0x11 form). Omit domain for an ordinary hash.
verify-hash: check a digest
POST /api/json/utilities/verify-hash / GraphQL verifyHash. Hashes the given text
and compares it to an expected hex value using a constant-time comparison. This is a
plain hash check only, never a domain-separated one.
Arguments: text (required), hash (required, the expected hex digest; surrounding
whitespace and letter case are normalized before comparison), and hash_type
(optional, default sha256, same twelve algorithms as hash).
Returns matches (boolean), computed (the hex digest of text), and algorithm.
uuidv7-timestamp: read a UUIDv7’s embedded time
POST /api/json/utilities/uuidv7-timestamp / GraphQL uuidv7Timestamp. Extracts the
time embedded in a UUIDv7 identifier when it was generated. Truestamp uses UUIDv7 for
blocks, entropy observations, memberships, and most other ids (see
ULID and UUIDv7 identifiers).
Arguments: uuid (required, a 36-character UUIDv7 string).
Returns iso8601, unix_ms, and the echoed uuid. An invalid UUIDv7 returns the
structured "ok": false failure. Note that an identifier’s embedded time is
informational: the timing Truestamp actually proves is
the submission window established by a verified
proof, not a timestamp parsed out of an id.
ulid-timestamp: read a ULID’s embedded time
POST /api/json/utilities/ulid-timestamp / GraphQL ulidTimestamp. Extracts the
time embedded in a ULID identifier when it was generated. Item
ids are the only ULIDs in Truestamp.
Arguments: ulid (required, a 26-character ULID string).
Returns iso8601, unix_ms, and the echoed ulid, with the same informational-time
caveat as uuidv7-timestamp. An invalid ULID returns the structured "ok": false
failure.
resolve-id: identify an opaque id (visibility-gated)
POST /api/json/utilities/resolve-id / GraphQL resolveId. Given a bare ULID or
UUIDv7, reports what it refers to so you can decide what to do with it. This is the
one utility that reads data, and it honors your authorization: a ULID can only be an
item, and it resolves only if the item is visible to you across your teams; a UUIDv7
may resolve to a block or an entropy observation (public ledger data, visible to any
authenticated caller) or to a team, but only one you are a member of. An id you
cannot see returns no match and its existence is never revealed.
Arguments: id (required, a 26-character ULID or 36-character UUIDv7).
Returns id, normalized_id, id_format ("ulid" or "uuidv7"), and matches, a
list where each entry has:
kind:"item","block","entropy_observation", or"team".verifiable: whether this kind of subject supports proofs at all. This is a property of the kind, not of the record’s current readiness: a freshly submitted item readsverifiable: truehere but cannot be proven until it is committed, so check its lifecycle state before generating a proof.proof_type: thetypeto pass when generating a proof (for example"item","block", or an entropy source type), or null for a non-verifiable kind.also_verifiable_as: alternate proof subjects, for example a block is also verifiable as a"beacon".summary: a compact, non-sensitive description of the match (id, state, and a few kind-specific fields).
A well-formed id that matches nothing you can access returns "matches": []. A
value that is neither a ULID nor a UUIDv7 returns the structured "ok": false
failure with "code": "invalid".
jcs-canonicalize: canonical JSON
POST /api/json/utilities/jcs-canonicalize / GraphQL jcsCanonicalize.
Canonicalizes a JSON object with JCS (RFC 8785): keys sorted,
insignificant whitespace removed, numbers and strings in canonical form. The
canonical string is the exact serialization Truestamp hashes, so this is the tool for
predicting hashes such as an item’s claims hash.
Arguments: json (required, the JSON object to canonicalize).
Returns canonical (the canonical JSON string) and byte_length.
One caveat when predicting a claims hash: pure JCS preserves null-valued keys, but
Truestamp drops null keys from item claims before hashing (see
the item’s composite fingerprint). To predict a
claims hash, remove null fields from the object first, canonicalize it here, then
hash the canonical string with the hash operation using domain items_claims.
server-time: the server’s current clock
POST /api/json/utilities/server-time / GraphQL serverTime. Returns the Truestamp
server’s current wall-clock time as an ISO 8601 string with second precision and an
explicit zone designator. Use it to coordinate with the server’s clock: to see how
long ago something was submitted, to interpret a returned timestamp in the reader’s
own zone, or simply to check that your client’s clock agrees with the server’s.
Arguments: time_zone (optional, default UTC). Any IANA time zone name, for example
America/New_York or Europe/Paris. Aliases are accepted and reported back
canonicalized: US/Pacific resolves to America/Los_Angeles, and UTC to Etc/UTC.
Returns:
iso8601: the time in the requested zone, for example"2026-01-15T07:34:56-05:00", or with aZsuffix in UTC. Never carries a fractional-second part.utc_iso8601: the same instant rendered in UTC.unix: the same instant as whole seconds since the Unix epoch.time_zone: the canonical IANA zone the time was rendered in.requested_time_zone: the value you passed, echoed back unchanged.utc_offset: the zone’s offset at that instant, for example"-05:00". Reflects daylight saving time, so the same zone returns different offsets across the year.abbreviation: the zone abbreviation at that instant, for example"EST". Zones that have no letter abbreviation in the IANA database report a numeric offset instead, so this is not always letters:Pacific/Chathamreturns"+1245"andPacific/Marquesasreturns"-0930".
An unrecognized zone returns the structured "ok": false failure with
"code": "invalid", and the message suggests canonical zone names that match what
you sent (so "New York" points at America/New_York).
This is an ordinary clock reading, offered for display and coordination. It is not evidence of anything and carries no cryptographic guarantee. The timing Truestamp actually proves is the submission window a verified proof establishes, and that is the only timing statement to rely on.
Citations
- RFC 8785: JSON Canonicalization Scheme (JCS). The canonicalization applied by
jcs-canonicalize. - RFC 9562: Universally Unique IDentifiers (UUIDs). Defines UUIDv7 and its embedded Unix-epoch millisecond timestamp, which
uuidv7-timestampextracts. - ULID Specification. Defines the ULID format and its 48-bit embedded timestamp, which
ulid-timestampextracts.