Public timestamps for your audit trail

Every day, GaaS stamps each organization's audit records into the Bitcoin blockchain through the free, public OpenTimestamps service. You can prove when your records existed, and that they have not changed since, without trusting GaaS.

What a timestamp proves

The hash chain and signatures show that no audit record was changed after it was written, as long as you trust the chain GaaS shows you. A public timestamp removes that last piece of trust. Once a day's stamp is in a Bitcoin block, nobody, GaaS included, can rewrite, add or remove a record of that day, or backdate one into it, without the proof failing. The block's time is independent evidence that the records existed no later than that moment.

What is stampedOne digest per organization per UTC day that has audit records (how it is computed).
WhenAfter the day ends, usually between 01:00 and 02:00 UTC the next day. If the calendars cannot be reached, GaaS tries again every hour for 7 days.
WhereFour public OpenTimestamps calendars: a.pool.opentimestamps.org, b.pool.opentimestamps.org, a.pool.eternitywall.com and ots.btc.catallaxy.com. At least two must accept a stamp. The calendars gather many stamps into one Bitcoin transaction.
What leaves GaaSOnly a 32-byte SHA-256 value computed from your digest and 16 random bytes, the same way the official ots stamp command does it. The calendars never see your digest, your records or your organization.
Whose data is in your proofOnly yours. Each organization's digest is stamped on its own.

The digest

Recipe, version 1. SHA-256 of a text file that lists the hash of every audit record of your organization whose created_at falls on that UTC day, one per line, sorted in ascending byte order, each line ending in a single newline (\n).

Sorting by hash rather than by time means you never parse timestamps or break ties: the same records always give the same file. And because the digest is simply the SHA-256 of that file, the official client checks the proof against the file itself. The API returns the recipe with every list of timestamps, as recipe.


Check a day yourself

1. Install the official client

pip install opentimestamps-client

This gives you the ots command. It reads and upgrades proofs without a Bitcoin node; the final check against a Bitcoin block needs one (step 5).

2. Export the day's records

The export needs an operator or admin key. Ask for the whole UTC day; end_date is inclusive.

DAY=2026-09-27
curl -s "https://api.gaas.is/v1/audit/export/stream?start_date=${DAY}T00:00:00Z&end_date=${DAY}T23:59:59.999999Z" \
  -H "X-API-Key: $OPERATOR_API_KEY" > $DAY.jsonl

Keep this export. Records older than your retention period are deleted from GaaS, and a timestamp can only ever be checked against the records themselves.

3. Build the hash file

jq -r '.hash // empty' $DAY.jsonl | LC_ALL=C sort > $DAY.hashes
shasum -a 256 $DAY.hashes

No jq? The same in Python, standard library only:

python3 - "$DAY" <<'EOF'
import hashlib, json, sys
day = sys.argv[1]
hashes = sorted(r["hash"] for r in map(json.loads, open(f"{day}.jsonl")) if r.get("hash"))
data = "".join(h + "\n" for h in hashes).encode()
open(f"{day}.hashes", "wb").write(data)
print(hashlib.sha256(data).hexdigest())
EOF

The value printed must equal that day's digest in the list from step 4, and the number of lines (wc -l < $DAY.hashes) its record_count.

4. Download the proof

Any key of your organization can list and download proofs (viewer and up).

curl -s https://api.gaas.is/v1/audit/timestamps -H "X-API-Key: $VIEWER_API_KEY"
curl -s -o $DAY.hashes.ots https://api.gaas.is/v1/audit/timestamps/$DAY/ots \
  -H "X-API-Key: $VIEWER_API_KEY"
ots info $DAY.hashes.ots

ots info starts with File sha256 hash: followed by the day's digest, then one branch per calendar that accepted the stamp. Until Bitcoin confirms it, each branch ends in a pending attestation:

File sha256 hash: 839275219773624b2e43907d801b406fdc7cee51599085da5043097f61940fa2
Timestamp:
append 4719bbf46102dc8ae5adb28bfe43c951
sha256
 -> append 3e4e44c0cb7295fa
    sha256
    …
    verify PendingAttestation('https://alice.btc.calendar.opentimestamps.org')
 -> append 6d08014fab242085
    …

5. Complete the proof and verify it

ots upgrade $DAY.hashes.ots
ots verify $DAY.hashes.ots

Before Bitcoin has confirmed the stamp, both commands say so, one line per calendar, and exit with an error. That is expected; try again later:

Calendar https://alice.btc.calendar.opentimestamps.org: Pending confirmation in Bitcoin blockchain
Calendar https://bob.btc.calendar.opentimestamps.org: Pending confirmation in Bitcoin blockchain
…
Failed! Timestamp not complete

How long it takes


API

EndpointRoleReturns
GET /v1/audit/timestampsviewer and upYour organization's stamped days, newest first (limit 1–366, default 90), and the recipe.
GET /v1/audit/timestamps/{day}/otsviewer and upThat day's proof as application/octet-stream, saved as {day}.hashes.ots, with the digest in the X-GaaS-Digest header. A day without a stamp, or another organization's day, is 404.
{
  "timestamps": [
    {
      "day": "2026-09-27",
      "digest": "839275219773624b2e43907d801b406fdc7cee51599085da5043097f61940fa2",
      "record_count": 3,
      "first_record_id": "aud_…",
      "last_record_id": "aud_…",
      "calendars": ["https://a.pool.opentimestamps.org", "https://b.pool.opentimestamps.org",
                    "https://a.pool.eternitywall.com", "https://ots.btc.catallaxy.com"],
      "stamped_at": "2026-09-28T01:05:12.000000+00:00",
      "proof_url": "/v1/audit/timestamps/2026-09-27/ots"
    }
  ],
  "total": 1,
  "recipe": { "version": 1, "digest": "SHA-256 of a text file listing …", … }
}

calendars lists the calendars that accepted the stamp. first_record_id and last_record_id are the day's first and last records by time.


Good to know


Sources

The OpenTimestamps details on this page were checked on 2026-09-28 against:


Related Pages