API reference

The API behind ghazaryantech.com: the JVM it runs on, and the lab's experiments, which run here on the server rather than in the browser. It is public and needs no key.

Base URL
https://ghazaryantech.com
Version
1

Before the first call

Versions

Every operation takes the API-Version header. The version of the API to answer with: 1 without the header. Any version but 1 is refused with 400 UNSUPPORTED_API_VERSION.

Limits

The experiments are limited per client address, each by a policy of its own, named in its entry. A response to one says where the client stands, and a refusal, 429, when to come back:

RateLimit-Policy
The operation's policy, per client address: its name, the quota q and the window w in seconds, as in "search";q=10;w=60.
RateLimit
Where the client stands in the policy: r requests left and t, the seconds until one more is allowed, as in "search";r=9;t=6. Sent with refusals too.
Retry-After
Seconds to wait before trying again: rounded up, never 0.

Streams

Some experiments report as they go: they answer text/event-stream, named events with JSON data, which each entry lists. A failure after the stream has begun arrives as an error event carrying a problem. A browser's EventSource opens a closed stream again, so close it on the last event.

Errors

An RFC 9457 problem: what went wrong, for a person in detail and for a program in code. Each entry lists its codes by status: a program switches on the code, never on the detail, which is written for people and may change.

typeoptional
stringThe kind of problem. Absent, it is about:blank: the status says it all.
title
stringThe status's reason phrase.
status
integerThe HTTP status.
detail
stringWhat went wrong, for a person.
instanceoptional
stringThe request's path; absent from a stream's error event.
code
stringWhat went wrong, for a program: stable across releases, unlike the detail.
{
  "detail": "n must be between 0 and 1000000",
  "instance": "/api/lab/fibonacci",
  "status": 400,
  "title": "Bad Request",
  "code": "N_OUT_OF_RANGE"
}

Runtime

The JVM this API runs on, and its database.

The database and its migrations

GET/api/database

The PostgreSQL server's version and the Flyway migrations applied: whether the database is there, and up to date. 503 without one.

Answers with
DatabaseInfo
Limit per address
30 requests a minute, policy database
Operation id
getDatabase

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-10-03. The response to GET /api/database. Arrays are cut to their first 4 items.

{
  "version": "17.11",
  "migrations": [
    {
      "version": "1",
      "description": "lab schema",
      "installedOn": "2026-10-03T05:23:22.166415Z",
      "success": true
    },
    {
      "version": "2",
      "description": "ledger",
      "installedOn": "2026-10-03T05:56:28.695017Z",
      "success": true
    },
    {
      "version": "3",
      "description": "catalogue",
      "installedOn": "2026-10-03T07:52:57.560606Z",
      "success": true
    },
    {
      "version": "4",
      "description": "outbox",
      "installedOn": "2026-10-03T16:00:34.010216Z",
      "success": true
    }
  ]
}

The JVM this API runs on

GET/api/runtime

Java version and vendor, the VM, Spring Boot's version, CPUs, the largest heap, the garbage collectors, start time, uptime, the Cloud Run revision, and how the JVM was started: its collector, its AOT cache, compact object headers, its -XX options (never system properties) and the memory it sees.

Answers with
RuntimeInfo
Limit per address
None
Operation id
getRuntime

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-10-03. The response to GET /api/runtime. Arrays are cut to their first 4 items.

{
  "javaVersion": "25.0.4.1",
  "javaVendor": "Eclipse Adoptium",
  "vm": "OpenJDK 64-Bit Server VM",
  "springBootVersion": "4.1.1",
  "availableProcessors": 1,
  "maxHeapBytes": 389283840,
  "garbageCollectors": [
    "Copy",
    "MarkSweepCompact"
  ],
  "startedAt": "2026-10-03T18:04:10.604Z",
  "uptimeMs": 6144,
  "revision": null,
  "jvm": {
    "collector": "Serial",
    "aotCache": "api.aot",
    "aotCacheInUse": true,
    "compactObjectHeaders": true,
    "flags": [
      "-XX:MaxRAMPercentage=75",
      "-XX:InitialRAMPercentage=25",
      "-XX:+ExitOnOutOfMemoryError",
      "-XX:+UseSerialGC"
    ],
    "memoryLimitBytes": 536870912
  }
}

One sample of the JVM

GET/api/telemetry

Heap and non-heap memory, collections and their time, threads, CPU load and loaded classes, now.

Answers with
TelemetrySample
Limit per address
None
Operation id
getTelemetry

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-10-03. The response to GET /api/telemetry.

{
  "t": 1791050656747,
  "uptimeMs": 6203,
  "heap": {
    "used": 28988208,
    "committed": 129761280,
    "max": 389283840
  },
  "nonHeapUsed": 23555400,
  "gc": [
    {
      "name": "Copy",
      "count": 4,
      "timeMs": 74
    },
    {
      "name": "MarkSweepCompact",
      "count": 0,
      "timeMs": 0
    }
  ],
  "threads": {
    "live": 22,
    "daemon": 18,
    "peak": 22
  },
  "cpu": {
    "process": 0.3,
    "system": 0.297
  },
  "classesLoaded": 11718
}

The JVM, every second

GET/api/telemetry/stream

Server-sent events: first history, up to the last 90 samples, oldest first; then sample every second; after 120 s, end with {"reason":"max-duration"}. Streams are capped in number. Close the stream on end: a browser's EventSource would otherwise open it again.

Answers with
server-sent events
Limit per address
20 requests a minute, policy telemetry-stream
Operation id
streamTelemetry

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • TOO_MANY_STREAMS As many live streams are open as the server serves.
A recorded stream

Recorded on 2026-10-03. The stream that answered GET /api/telemetry/stream. Of its 123 events, the first and the last of each kind.

event:history
data:[{"t":1791050656059,"uptimeMs":5515,"heap":{"used":41680216,"committed":129761280,"max":389283840},"nonHeapUsed":15566672,"gc":[{"name":"Copy","count":3,"timeMs":59},{"name":"MarkSweepCompact","count":0,"timeMs":0}],"threads":{"live":16,"daemon":12,"peak":16},"cpu":{"process":0.975,"system":0.978},"classesLoaded":10568}]

event:sample
data:{"t":1791050656700,"uptimeMs":6157,"heap":{"used":27984808,"committed":129761280,"max":389283840},"nonHeapUsed":23348312,"gc":[{"name":"Copy","count":4,"timeMs":74},{"name":"MarkSweepCompact","count":0,"timeMs":0}],"threads":{"live":22,"daemon":18,"peak":22},"cpu":{"process":1.0,"system":1.0},"classesLoaded":11681}

: 119 events left out

event:sample
data:{"t":1791050776053,"uptimeMs":125515,"heap":{"used":41824256,"committed":129761280,"max":389283840},"nonHeapUsed":41230288,"gc":[{"name":"Copy","count":12,"timeMs":108},{"name":"MarkSweepCompact","count":0,"timeMs":0}],"threads":{"live":24,"daemon":20,"peak":226},"cpu":{"process":0.025,"system":0.003},"classesLoaded":12920}

event:end
data:{"reason":"max-duration"}

Lab: the JVM

Experiments on the server's JVM: each answers with what it measured, on this machine, just now.

Five ways to compute F(n)

GET/api/lab/fibonacci

Naive recursion, memoized recursion, a loop, matrix exponentiation and fast doubling, on BigInteger. Each reports OK with the median of up to five timed runs after warm-up, or SKIPPED with the reason, STACK_OVERFLOW or TIMEOUT. Two races compute at a time; a third waits two seconds, then 429 BUSY.

Answers with
FibonacciReport
Limit per address
30 requests a minute, policy fibonacci
Operation id
runFibonacci

Parameters

nquery, required
integer0 to 1,000,000
Which Fibonacci number

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • N_OUT_OF_RANGE n is outside 0 to 1,000,000.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-10-03. The response to GET /api/lab/fibonacci?n=90. Arrays are cut to their first 4 items.

{
  "n": 90,
  "digits": 19,
  "leadingDigits": "2880067194370816120",
  "trailingDigits": "2880067194370816120",
  "value": "2880067194370816120",
  "results": [
    {
      "algorithm": "NAIVE_RECURSION",
      "label": "Naive recursion",
      "complexity": "O(φⁿ)",
      "status": "SKIPPED",
      "nanos": null,
      "runs": 0,
      "operations": null,
      "maxDepth": null,
      "note": "Skipped above n = 40: would need ≈9.3×10^18 calls"
    },
    {
      "algorithm": "MEMOIZED_RECURSION",
      "label": "Memoized recursion",
      "complexity": "O(n)",
      "status": "OK",
      "nanos": 10653,
      "runs": 5,
      "operations": 179,
      "maxDepth": 90,
      "note": null
    },
    {
      "algorithm": "ITERATIVE",
      "label": "Iterative",
      "complexity": "O(n) additions",
      "status": "OK",
      "nanos": 4695,
      "runs": 5,
      "operations": 89,
      "maxDepth": null,
      "note": null
    },
    {
      "algorithm": "MATRIX_POWER",
      "label": "Matrix exponentiation",
      "complexity": "O(log n) multiplications",
      "status": "OK",
      "nanos": 16122,
      "runs": 5,
      "operations": 80,
      "maxDepth": null,
      "note": null
    }
  ],
  "environment": {
    "javaVersion": "25.0.4.1",
    "availableProcessors": 1
  }
}

Watch the JIT compile, then deoptimise

GET/api/lab/jit

A cold copy of a small loop, in a class loader of its own, timed in 240 batches as its call site meets one, two, then four receiver types, with the compilations and deoptimisations Flight Recorder reported for it, each placed in its batch. One benchmark at a time on the server.

Answers with
JitReport
Limit per address
10 requests a minute, policy jit
Operation id
runJit

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • JIT_UNAVAILABLE Flight Recorder is not available on this server.
A recorded response

Recorded on 2026-10-03. The response to GET /api/lab/jit. Arrays are cut to their first 4 items.

{
  "completed": true,
  "callsPerBatch": 20000,
  "batches": [
    {
      "index": 0,
      "shape": "MONOMORPHIC",
      "nanos": 487827
    },
    {
      "index": 1,
      "shape": "MONOMORPHIC",
      "nanos": 301288
    },
    {
      "index": 2,
      "shape": "MONOMORPHIC",
      "nanos": 323336
    },
    {
      "index": 3,
      "shape": "MONOMORPHIC",
      "nanos": 292164
    }
  ],
  "events": [
    {
      "batch": 0,
      "kind": "COMPILED",
      "method": "Shapes.Circle.<init>",
      "detail": "c1, tier 2"
    },
    {
      "batch": 0,
      "kind": "COMPILED",
      "method": "Shapes.Circle.area",
      "detail": "c1, tier 2"
    },
    {
      "batch": 3,
      "kind": "COMPILED",
      "method": "Shapes.totalArea",
      "detail": "c1, tier 3, on-stack replacement"
    },
    {
      "batch": 3,
      "kind": "COMPILED",
      "method": "Shapes.totalArea",
      "detail": "c1, tier 3"
    }
  ],
  "summary": {
    "firstNanos": 487827,
    "monomorphicNanos": 66602,
    "bimorphicNanos": 71202,
    "megamorphicNanos": 54182
  }
}

A memory leak on a real collector

GET/api/lab/leak/stream

Server-sent events: a JVM of its own, 64 MB of heap, runs the leak or its fix under Serial, G1 or ZGC. start; gc for every collection that reports the heap; stopped for every quarter second in which the application stood still; then end with OUT_OF_MEMORY and when, or SURVIVED after 20 s; or error.

Answers with
server-sent events
Limit per address
6 requests a minute, policy leak
Operation id
streamLeak

Parameters

collectorquery, required
SERIAL, G1 or Z
The garbage collector the child JVM runs
policyquery, required
LEAK or FIX
LEAK, a map that never lets go; FIX, weak values with a time to live

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • LEAK_UNAVAILABLE The server cannot start the JVM a run needs.
erroran event, once the stream has begun
  • LEAK_UNAVAILABLE The server cannot start the JVM a run needs.
  • CANCELLED The run was stopped before it finished, as when the server shuts down.
  • TIMEOUT The run took too long and was stopped.
  • CHILD_FAILED The run's JVM ended without finishing it.
A recorded stream

Recorded on 2026-10-03. The stream that answered GET /api/lab/leak/stream?collector=Z&policy=FIX. Of its 130 events, the first and the last of each kind.

event:start
data:{"collector":"Z","policy":"FIX","heapMB":64,"seconds":20}

event:gc
data:{"at":90,"gcId":0,"name":"Major Collection (Warmup)","beforeMB":8,"afterMB":8,"includesOld":true}

: 2 events left out

event:stopped
data:{"from":0,"stopped":{"pauses":15,"pauseMillis":0.128,"longestPauseMillis":0.063,"stalls":0,"stallMillis":0.0}}

: 122 events left out

event:gc
data:{"at":19678,"gcId":70,"name":"Major Collection (Proactive)","beforeMB":32,"afterMB":18,"includesOld":true}

event:stopped
data:{"from":19500,"stopped":{"pauses":8,"pauseMillis":0.058,"longestPauseMillis":0.015,"stalls":0,"stallMillis":0.0}}

event:end
data:{"outcome":"SURVIVED","atMillis":20000,"collections":71,"oldCollections":49,"stopped":{"pauses":311,"pauseMillis":1.971,"longestPauseMillis":0.063,"stalls":0,"stallMillis":0.0}}

Where the time goes, by Flight Recorder

GET/api/lab/profiler

A scenario profiled while it runs: its flame graph, hottest frames, monitor waits, allocation by class and collections, as the problem or as its fix. One benchmark at a time on the server.

Answers with
ProfileReport
Limit per address
10 requests a minute, policy profiler
Operation id
runProfiler

Parameters

scenarioquery, required
HOT_LOCK, ALLOCATION_CHURN or QUADRATIC_DEDUPE
The workload to profile
variantquery, required
PROBLEM or FIX
PROBLEM, as written first; FIX, as it should be

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • INTERRUPTED The run was interrupted, as when the server shuts down.
  • PROFILER_UNAVAILABLE Flight Recorder is not available on this server.
A recorded response

Recorded on 2026-10-03. The response to GET /api/lab/profiler?scenario=HOT_LOCK&variant=PROBLEM.

{
  "scenario": "HOT_LOCK",
  "variant": "PROBLEM",
  "completed": true,
  "wallMs": 2274,
  "threads": 4,
  "checksum": "10000000",
  "samples": 199,
  "tree": {
    "name": "all",
    "total": 199,
    "self": 0,
    "children": [
      {
        "name": "HotLock.incrementUnderMonitor",
        "total": 199,
        "self": 83,
        "children": [
          {
            "name": "HotLock.Counter.increment",
            "total": 116,
            "self": 116,
            "children": []
          }
        ]
      }
    ]
  },
  "hottest": [
    {
      "frame": "HotLock.Counter.increment",
      "self": 116,
      "share": 0.583
    },
    {
      "frame": "HotLock.incrementUnderMonitor",
      "self": 83,
      "share": 0.417
    }
  ],
  "locks": {
    "waits": 8266,
    "totalMs": 3187,
    "monitor": "HotLock.Counter"
  },
  "allocations": {
    "sampledBytes": 0,
    "byClass": []
  },
  "gc": {
    "collections": 1,
    "pauseTotalMs": 10,
    "longestPauseMs": 10
  }
}

Platform threads against virtual threads

GET/api/lab/threads/stream

Server-sent events: the same workload on a fixed pool of platform threads, then on virtual threads. start and result for each, progress between, then done. Each result carries pinnedEvents, the virtual phase's pinning count from Flight Recorder. One benchmark at a time on the server.

Answers with
server-sent events
Limit per address
6 requests a minute, policy threads
Operation id
streamThreads

Parameters

tasksquery, required
integer1 to 20,000
Tasks to run
ioMillisquery, required
integer1 to 1,000
Milliseconds each task sleeps, or computes
poolSizequery, required
integer1 to 1,000
Platform threads in the fixed pool
workloadquery, required
IO, SYNCHRONIZED, NATIVE or CPU
Sleep, sleep holding a monitor, sleep beneath a native frame, or compute

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • TASKS_OUT_OF_RANGE tasks is outside 1 to 20,000.
  • IO_MILLIS_OUT_OF_RANGE ioMillis is outside 1 to 1,000.
  • POOL_SIZE_OUT_OF_RANGE poolSize is outside 1 to 1,000.
  • CPU_BUDGET_EXCEEDED The CPU workload asks for more computation than a run may spend: tasks × ioMillis is capped.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • NATIVE_UNAVAILABLE The NATIVE workload's native frame could not be linked on this server.
erroran event, once the stream has begun
  • CANCELLED The run was stopped before it finished, as when the server shuts down.
  • INTERNAL_ERROR Something failed on the server's side.
A recorded stream

Recorded on 2026-10-03. The stream that answered GET /api/lab/threads/stream?tasks=2000&ioMillis=20&poolSize=100&workload=IO. Of its 19 events, the first and the last of each kind.

event:start
data:{"executor":"PLATFORM","tasks":2000,"poolSize":100,"workload":"IO","ioMillis":20,"carrierThreads":1}

event:progress
data:{"executor":"PLATFORM","completed":400,"elapsedMs":100}

: 3 events left out

event:result
data:{"executor":"PLATFORM","tasks":2000,"completed":2000,"wallMs":415,"throughputPerSec":4817.5,"waitP50Ms":180,"waitP99Ms":378,"timedOut":false,"peakLiveThreads":128,"pinnedEvents":null}

event:start
data:{"executor":"VIRTUAL","tasks":2000,"poolSize":null,"workload":"IO","ioMillis":20,"carrierThreads":1}

: 9 events left out

event:progress
data:{"executor":"VIRTUAL","completed":2000,"elapsedMs":997}

event:result
data:{"executor":"VIRTUAL","tasks":2000,"completed":2000,"wallMs":25,"throughputPerSec":78456.2,"waitP50Ms":1,"waitP99Ms":3,"timedOut":false,"peakLiveThreads":28,"pinnedEvents":0}

event:done
data:{}

The ThreadLocal trap

GET/api/lab/threads/thread-locals

For each way of getting a date formatter, on each executor, the formatters created, heap allocated per task and wall time: a ThreadLocal cache that works on a pool of platform threads creates one per task on virtual threads.

Answers with
ThreadLocalReport
Limit per address
6 requests a minute, policy threads
Operation id
runThreadLocals

Parameters

tasksquery, required
integer1 to 20,000
Tasks to run
poolSizequery, required
integer1 to 1,000
Platform threads in the fixed pool

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • TASKS_OUT_OF_RANGE tasks is outside 1 to 20,000.
  • POOL_SIZE_OUT_OF_RANGE poolSize is outside 1 to 1,000.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • INTERRUPTED The run was interrupted, as when the server shuts down.
A recorded response

Recorded on 2026-10-03. The response to GET /api/lab/threads/thread-locals?tasks=10000&poolSize=200. Arrays are cut to their first 4 items.

{
  "tasks": 10000,
  "poolSize": 200,
  "carrierThreads": 1,
  "cells": [
    {
      "source": "THREAD_LOCAL",
      "executor": "PLATFORM",
      "formattersCreated": 200,
      "allocatedBytesPerTask": 706,
      "wallMicros": 210484
    },
    {
      "source": "THREAD_LOCAL",
      "executor": "VIRTUAL",
      "formattersCreated": 10000,
      "allocatedBytesPerTask": 2831,
      "wallMicros": 116645
    },
    {
      "source": "PER_CALL",
      "executor": "PLATFORM",
      "formattersCreated": 10000,
      "allocatedBytesPerTask": 2394,
      "wallMicros": 269622
    },
    {
      "source": "PER_CALL",
      "executor": "VIRTUAL",
      "formattersCreated": 10000,
      "allocatedBytesPerTask": 2700,
      "wallMicros": 27316
    }
  ]
}

Lab: PostgreSQL

Experiments on a real PostgreSQL: a connection pool, transactions, search and delivery.

Reconcile the ledger

GET/api/lab/ledger/reconciliation

The drift between cached balances and entries, as the last run left it.

Answers with
Reconciliation
Limit per address
30 requests a minute, policy database
Operation id
getReconciliation

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_NOT_MIGRATED The database lacks the experiment's tables: its migrations have not run.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-10-03. The response to GET /api/lab/ledger/reconciliation.

{
  "accountsDrifted": 0,
  "moneyCreated": 0,
  "moneyDestroyed": 0,
  "worst": []
}

A ledger under concurrent transfers

POST/api/lab/ledger/runs

Resets the ledger, makes the same 1,000 transfers from 16 connections under one strategy, then checks four invariants by SQL and reconciles cached balances against entries. A POST: it rewrites the ledger. The response names the run's traceId.

Answers with
LedgerReport
Limit per address
6 requests a minute, policy ledger
Operation id
runLedger

Parameters

strategyquery, required
READ_MODIFY_WRITE, GUARDED_UPDATE, LOCK_UNORDERED, LOCK_ORDERED, OPTIMISTIC or SERIALIZABLE
How a transfer moves the money
hotSpotquery, required
integer0 to 100
The share of transfers between four hot accounts, in per cent

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • HOT_SPOT_OUT_OF_RANGE hotSpot is outside 0 to 100.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_NOT_MIGRATED The database lacks the experiment's tables: its migrations have not run.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
  • INTERRUPTED The run was interrupted, as when the server shuts down.
A recorded response

Recorded on 2026-10-03. The response to POST /api/lab/ledger/runs?strategy=LOCK_ORDERED&hotSpot=20.

{
  "strategy": "LOCK_ORDERED",
  "hotSpotPercent": 20,
  "transfers": 1000,
  "workers": 16,
  "committed": 1000,
  "insufficientFunds": 0,
  "gaveUp": 0,
  "failed": 0,
  "notStarted": 0,
  "deadlocks": 0,
  "serializationFailures": 0,
  "conflicts": 0,
  "elapsedMs": 1115,
  "throughput": 896.2,
  "latencyP50Ms": 4,
  "latencyP99Ms": 158,
  "invariants": [
    {
      "name": "Money is neither created nor destroyed",
      "holds": true,
      "detail": "100,000.00 in all, as at the start"
    },
    {
      "name": "No balance below zero",
      "holds": true,
      "detail": "every account covers itself"
    },
    {
      "name": "Every transfer’s entries sum to zero",
      "holds": true,
      "detail": "the database refuses any that do not"
    },
    {
      "name": "Balances agree with the entries",
      "holds": true,
      "detail": "reconciliation finds nothing"
    }
  ],
  "reconciliation": {
    "accountsDrifted": 0,
    "moneyCreated": 0,
    "moneyDestroyed": 0,
    "worst": []
  },
  "traceId": "768f8b85a124217574c2aa423fd75119"
}

An outbox under crash injection

POST/api/lab/outbox/runs

Places 500 orders under one of three designs while processes die at chosen moments, then counts on the database what was lost, invented, sent again and done again. A POST: it empties the experiment's tables.

Answers with
OutboxReport
Limit per address
6 requests a minute, policy outbox
Operation id
runOutbox

Parameters

designquery, required
COMMIT_THEN_PUBLISH, PUBLISH_THEN_COMMIT or OUTBOX
How the service saves and announces an order
crashPercentquery, required
integer0 to 50
The chance of dying at each crash point, each time one is reached
dedupquery
booleantrue by default
Whether the consumer acts on each message id once

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • CRASH_PERCENT_OUT_OF_RANGE crashPercent is outside 0 to 50.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_NOT_MIGRATED The database lacks the experiment's tables: its migrations have not run.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
  • INTERRUPTED The run was interrupted, as when the server shuts down.
A recorded response

Recorded on 2026-10-03. The response to POST /api/lab/outbox/runs?design=OUTBOX&crashPercent=10&dedup=true.

{
  "design": "OUTBOX",
  "crashPercent": 10,
  "dedup": true,
  "orders": 500,
  "answered": 446,
  "committed": 446,
  "published": 446,
  "duplicatePublishes": 58,
  "lost": 0,
  "phantoms": 0,
  "deliveries": 566,
  "effects": 446,
  "duplicateEffects": 0,
  "missingEffects": 0,
  "effectsWithoutOrder": 0,
  "crashes": {
    "BETWEEN_WRITES": 0,
    "RELAY_AFTER_PUBLISH": 58,
    "CONSUMER_AFTER_EFFECT": 62,
    "BEFORE_COMMIT": 54
  },
  "restarts": 120,
  "elapsedMs": 2602,
  "verdicts": [
    {
      "name": "Every saved order was announced",
      "holds": true,
      "detail": "the broker has an event for every order"
    },
    {
      "name": "Nothing was announced that did not happen",
      "holds": true,
      "detail": "every event has its order"
    },
    {
      "name": "Every order was acted on exactly once",
      "holds": true,
      "detail": "one action per saved order, none for anything else"
    }
  ]
}

Ten thousand threads, twenty connections

GET/api/lab/pool/stream

Server-sent events: requests on virtual threads against a PostgreSQL pool of the run's own, for 5 s, under an admission policy. start (with the pool's ceiling by Little's law, and the run's traceId), progress every 200 ms, result, then done; error when the pool cannot be opened.

Answers with
server-sent events
Limit per address
6 requests a minute, policy pool
Operation id
streamPool

Parameters

poolSizequery, required
integer5 to 20
Connections in the run's pool
holdMillisquery, required
integer10 to 500
How long each request holds its connection
ratequery, required
integer10 to 1,000
Requests arriving each second
policyquery, required
UNBOUNDED, THROTTLE or FAIL_FAST
What a request does when every connection is taken

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • POOL_SIZE_OUT_OF_RANGE poolSize is outside 5 to 20.
  • HOLD_MILLIS_OUT_OF_RANGE holdMillis is outside 10 to 500.
  • RATE_OUT_OF_RANGE rate is outside 10 to 1,000.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
erroran event, once the stream has begun
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
  • CANCELLED The run was stopped before it finished, as when the server shuts down.
  • INTERNAL_ERROR Something failed on the server's side.
A recorded stream

Recorded on 2026-10-03. The stream that answered GET /api/lab/pool/stream?poolSize=10&holdMillis=50&rate=500&policy=THROTTLE. Of its 67 events, the first and the last of each kind.

event:start
data:{"spec":{"poolSize":10,"holdMillis":50,"ratePerSecond":500,"policy":"THROTTLE"},"requests":2500,"capacityPerSecond":200.0,"connectionTimeoutMs":1000,"traceId":"5fb11f17c661cff72c3895fd7b917c95"}

event:progress
data:{"elapsedMs":201,"arrived":101,"completed":30,"timedOut":0,"rejected":0,"holding":10,"waitingForConnection":0,"waitingAtGate":61}

: 62 events left out

event:progress
data:{"elapsedMs":12683,"arrived":2500,"completed":2500,"timedOut":0,"rejected":0,"holding":0,"waitingForConnection":0,"waitingAtGate":0}

event:result
data:{"requests":2500,"completed":2500,"timedOut":0,"rejected":0,"cutOff":0,"failed":0,"elapsedMs":12683,"throughput":197.1,"capacityPerSecond":200.0,"latencyP50Ms":3861,"latencyP99Ms":7627,"acquireP50Ms":0,"acquireP99Ms":0}

event:done
data:{}

Search a million rows four ways

GET/api/lab/search

The same words in a catalogue of a million products: ILIKE on every row, the same ILIKE on a trigram index, word similarity, full text. Each counts every match, returns ten, and is explained by PostgreSQL.

Answers with
SearchReport
Limit per address
10 requests a minute, policy search
Operation id
runSearch

Parameters

qquery, required
string1 to 60 charactersmatches ^[^\x00-\x1F\x7F]*$
The words, trimmed: at most 60 characters, no control characters

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • Q_INVALID q breaks a constraint its schema gives.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
  • BUSY The server is running another benchmark, or its lab workers are all taken.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_NOT_MIGRATED The database lacks the experiment's tables: its migrations have not run.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-10-03. The response to GET /api/lab/search?q=walnut%20desk. Arrays are cut to their first 4 items.

{
  "query": "walnut desk",
  "catalogue": {
    "products": 1000000,
    "bytes": 262594560
  },
  "methods": [
    {
      "method": "SCAN",
      "outcome": "COMPLETED",
      "total": 1995,
      "top": [
        {
          "id": 776484,
          "title": "Alder & Co Scandinavian walnut desk, charcoal",
          "category": "Tables",
          "priceCents": 38356,
          "score": null
        },
        {
          "id": 386607,
          "title": "Alder & Co Scandinavian walnut desk, ivory",
          "category": "Tables",
          "priceCents": 43950,
          "score": null
        },
        {
          "id": 54282,
          "title": "Alder & Co Scandinavian walnut desk, ochre",
          "category": "Tables",
          "priceCents": 95455,
          "score": null
        },
        {
          "id": 173738,
          "title": "Alder & Co classic walnut desk, terracotta",
          "category": "Tables",
          "priceCents": 29096,
          "score": null
        }
      ],
      "plan": {
        "access": "EVERY_ROW",
        "index": null,
        "workers": 2,
        "rowsRead": 999999,
        "blocksHit": 4163,
        "blocksRead": 17396,
        "planningMs": 0.126,
        "executionMs": 424.019,
        "steps": [
          {
            "depth": 0,
            "node": "Limit",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 1,
            "node": "WindowAgg",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 2,
            "node": "Gather Merge",
            "on": null,
            "rows": 1995,
            "loops": 1
          },
          {
            "depth": 3,
            "node": "Sort",
            "on": null,
            "rows": 665,
            "loops": 3
          }
        ]
      }
    },
    {
      "method": "TRIGRAM",
      "outcome": "COMPLETED",
      "total": 1995,
      "top": [
        {
          "id": 776484,
          "title": "Alder & Co Scandinavian walnut desk, charcoal",
          "category": "Tables",
          "priceCents": 38356,
          "score": null
        },
        {
          "id": 386607,
          "title": "Alder & Co Scandinavian walnut desk, ivory",
          "category": "Tables",
          "priceCents": 43950,
          "score": null
        },
        {
          "id": 54282,
          "title": "Alder & Co Scandinavian walnut desk, ochre",
          "category": "Tables",
          "priceCents": 95455,
          "score": null
        },
        {
          "id": 173738,
          "title": "Alder & Co classic walnut desk, terracotta",
          "category": "Tables",
          "priceCents": 29096,
          "score": null
        }
      ],
      "plan": {
        "access": "INDEX",
        "index": "products_title_trgm",
        "workers": 0,
        "rowsRead": 1995,
        "blocksHit": 2175,
        "blocksRead": 0,
        "planningMs": 0.109,
        "executionMs": 9.666,
        "steps": [
          {
            "depth": 0,
            "node": "Limit",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 1,
            "node": "Sort",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 2,
            "node": "WindowAgg",
            "on": null,
            "rows": 1995,
            "loops": 1
          },
          {
            "depth": 3,
            "node": "Bitmap Heap Scan",
            "on": "products",
            "rows": 1995,
            "loops": 1
          }
        ]
      }
    },
    {
      "method": "TYPO",
      "outcome": "COMPLETED",
      "total": 1995,
      "top": [
        {
          "id": 899,
          "title": "Fenwick classic walnut desk, black",
          "category": "Tables",
          "priceCents": 71261,
          "score": 1
        },
        {
          "id": 1245,
          "title": "Brume vintage walnut desk, ochre",
          "category": "Tables",
          "priceCents": 84226,
          "score": 1
        },
        {
          "id": 1339,
          "title": "Alder & Co curved walnut desk, white",
          "category": "Tables",
          "priceCents": 91197,
          "score": 1
        },
        {
          "id": 1566,
          "title": "Ashby tall walnut desk, white",
          "category": "Tables",
          "priceCents": 38831,
          "score": 1
        }
      ],
      "plan": {
        "access": "INDEX",
        "index": "products_title_trgm",
        "workers": 0,
        "rowsRead": 3597,
        "blocksHit": 3707,
        "blocksRead": 0,
        "planningMs": 0.383,
        "executionMs": 37.53,
        "steps": [
          {
            "depth": 0,
            "node": "Limit",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 1,
            "node": "Sort",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 2,
            "node": "WindowAgg",
            "on": null,
            "rows": 1995,
            "loops": 1
          },
          {
            "depth": 3,
            "node": "Bitmap Heap Scan",
            "on": "products",
            "rows": 1995,
            "loops": 1
          }
        ]
      }
    },
    {
      "method": "FULL_TEXT",
      "outcome": "COMPLETED",
      "total": 1995,
      "top": [
        {
          "id": 899,
          "title": "Fenwick classic walnut desk, black",
          "category": "Tables",
          "priceCents": 71261,
          "score": 0.09910322
        },
        {
          "id": 1245,
          "title": "Brume vintage walnut desk, ochre",
          "category": "Tables",
          "priceCents": 84226,
          "score": 0.09910322
        },
        {
          "id": 1339,
          "title": "Alder & Co curved walnut desk, white",
          "category": "Tables",
          "priceCents": 91197,
          "score": 0.09910322
        },
        {
          "id": 1566,
          "title": "Ashby tall walnut desk, white",
          "category": "Tables",
          "priceCents": 38831,
          "score": 0.09910322
        }
      ],
      "plan": {
        "access": "INDEX",
        "index": "products_search",
        "workers": 0,
        "rowsRead": 1995,
        "blocksHit": 2027,
        "blocksRead": 0,
        "planningMs": 0.05,
        "executionMs": 4.428,
        "steps": [
          {
            "depth": 0,
            "node": "Limit",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 1,
            "node": "Sort",
            "on": null,
            "rows": 10,
            "loops": 1
          },
          {
            "depth": 2,
            "node": "WindowAgg",
            "on": null,
            "rows": 1995,
            "loops": 1
          },
          {
            "depth": 3,
            "node": "Bitmap Heap Scan",
            "on": "products",
            "rows": 1995,
            "loops": 1
          }
        ]
      }
    }
  ]
}

Lab: payments

A simulated 3-D Secure 2 access control server: an AReq checked, weighed against the exemptions from strong customer authentication, and answered, with a challenge where one is due.

Send an AReq to a simulated ACS

POST/api/lab/threeds/authentications

An AReq of EMV 3-D Secure 2.2.0, as a 3DS Server would send it: a JSON object of string elements, a subset of the specification's, flat; at most 80 of them, each at most 4,096 characters. The simulated ACS checks it, answering an invalid one with an Erro; asks its risk rules, among them the exemptions from strong customer authentication; and answers with an ARes: authenticated without friction, a challenge, not authenticated or rejected. Every message on the way, the rules' reasoning and the time each hop takes.

Answers with
ThreeDsRun
Limit per address
30 requests a minute, policy threeds
Operation id
runThreeDsAuthentication

Body

application/jsonrequired
map of stringat most 80 entriesnames up to 64 charactersvalues up to 4,096 characters

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • UNREADABLE_BODY The body is not the JSON the operation takes.
  • AREQ_INVALID areq breaks a constraint its schema gives.
413Content Too Large
  • CONTENT_TOO_LARGE The body is larger than any operation takes: the API reads 2 MB of JSON at most.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-10-04. The response to POST /api/lab/threeds/authentications. Arrays are cut to their first 4 items.

{
  "acsTransID": "4047e5c5-b8d6-4f1f-b971-1dc97734deb3",
  "phase": "CHALLENGED",
  "transStatus": null,
  "steps": [
    {
      "from": "3DS Server",
      "to": "ACS",
      "via": "Directory Server",
      "message": "AReq",
      "fields": {
        "messageType": "AReq",
        "messageVersion": "2.2.0",
        "deviceChannel": "02",
        "messageCategory": "01",
        "threeDSServerTransID": "8a880dc0-d2d2-4067-bcb1-b08d1690b26e",
        "threeDSServerRefNumber": "3DS-SERVER-EXAMPLE-01",
        "threeDSServerURL": "https://3ds-server.example/results",
        "threeDSRequestorID": "requestor-0001",
        "threeDSRequestorName": "Example Shop",
        "threeDSRequestorURL": "https://shop.example",
        "threeDSRequestorAuthenticationInd": "01",
        "threeDSCompInd": "Y",
        "acctNumber": "4111111111111111",
        "acquirerBIN": "400551",
        "acquirerMerchantID": "merchant-0001",
        "mcc": "5732",
        "merchantCountryCode": "276",
        "merchantName": "Example Shop",
        "purchaseAmount": "75000",
        "purchaseCurrency": "978",
        "purchaseExponent": "2",
        "purchaseDate": "20261004120000",
        "browserAcceptHeader": "text/html,application/xhtml+xml",
        "browserJavaEnabled": "false",
        "browserJavascriptEnabled": "true",
        "browserLanguage": "en-GB",
        "browserColorDepth": "24",
        "browserScreenHeight": "900",
        "browserScreenWidth": "1440",
        "browserTZ": "-240",
        "browserUserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
        "notificationURL": "https://shop.example/3ds/notify",
        "addrMatch": "Y",
        "dsTransID": "6224dbca-ce3e-4e0e-a3c3-5e2be56a98db",
        "dsReferenceNumber": "DS-SIMULATED-01",
        "dsURL": "https://ds.example/results"
      },
      "note": "As the 3DS Server sent it, with what the directory server adds on the way: dsTransID, dsReferenceNumber and dsURL."
    },
    {
      "from": "ACS",
      "to": "3DS Server",
      "via": "Directory Server",
      "message": "ARes",
      "fields": {
        "messageType": "ARes",
        "messageVersion": "2.2.0",
        "threeDSServerTransID": "8a880dc0-d2d2-4067-bcb1-b08d1690b26e",
        "acsTransID": "4047e5c5-b8d6-4f1f-b971-1dc97734deb3",
        "acsReferenceNumber": "ACS-SIMULATED-01",
        "dsTransID": "6224dbca-ce3e-4e0e-a3c3-5e2be56a98db",
        "dsReferenceNumber": "DS-SIMULATED-01",
        "transStatus": "C",
        "acsChallengeMandated": "Y",
        "authenticationType": "02",
        "acsURL": "https://acs.example/challenge"
      },
      "note": "A challenge: the 3DS Server is to send the cardholder’s browser to the acsURL."
    },
    {
      "from": "3DS Server",
      "to": "ACS",
      "via": "Browser",
      "message": "CReq",
      "fields": {
        "messageType": "CReq",
        "messageVersion": "2.2.0",
        "threeDSServerTransID": "8a880dc0-d2d2-4067-bcb1-b08d1690b26e",
        "acsTransID": "4047e5c5-b8d6-4f1f-b971-1dc97734deb3",
        "challengeWindowSize": "05"
      },
      "note": "Posted through the cardholder’s browser to the acsURL."
    },
    {
      "from": "ACS",
      "to": "Browser",
      "via": null,
      "message": "Challenge page",
      "fields": {
        "page": "Enter the code we sent by text message to •••• 0042."
      },
      "note": "The ACS’s own page in the cardholder’s browser, not a 3-D Secure message."
    }
  ],
  "rules": [
    {
      "rule": "Card on record",
      "decided": false,
      "words": "The issuer knows the card."
    },
    {
      "rule": "Not reported stolen",
      "decided": false,
      "words": "The card is not reported lost or stolen."
    },
    {
      "rule": "Cardholder present",
      "decided": false,
      "words": "The cardholder is there, in a browser or an app."
    },
    {
      "rule": "Requestor’s mandate",
      "decided": false,
      "words": "The requestor mandates no challenge."
    }
  ],
  "challenge": {
    "sentTo": "•••• 0042",
    "code": "845323",
    "triesLeft": 3
  },
  "valueCheck": null,
  "budget": [
    {
      "name": "AReq: 3DS Server to directory server",
      "millis": 40,
      "measured": false
    },
    {
      "name": "AReq: directory server to ACS",
      "millis": 40,
      "measured": false
    },
    {
      "name": "The ACS: the check and the rules",
      "millis": 44.64375,
      "measured": true
    },
    {
      "name": "ARes: ACS to directory server",
      "millis": 40,
      "measured": false
    }
  ]
}

Answer a challenge

POST/api/lab/threeds/authentications/{acsTransID}/challenge

The cardholder's code for a transaction the ACS challenged, for five minutes after it did. A right code ends the challenge authenticated, a third wrong one ends it failed; either way with the RReq, the RRes and the final CRes.

Answers with
ThreeDsRun
Limit per address
30 requests a minute, policy threeds
Operation id
runThreeDsChallenge

Parameters

acsTransIDpath, required
stringmatches ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
The ACS's id for the transaction, from its ARes

Body

application/jsonrequired
ThreeDsLabController.Answer

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • UNREADABLE_BODY The body is not the JSON the operation takes.
  • ACS_TRANS_ID_INVALID acsTransID breaks a constraint its schema gives.
  • CODE_INVALID code breaks a constraint its schema gives.
404Not Found
  • CHALLENGE_NOT_FOUND No challenge with that id is waiting: it ended, its five minutes passed, or it never began.
409Conflict
  • CHALLENGE_ENDED Another answer ended the challenge while this one was on its way.
413Content Too Large
  • CONTENT_TOO_LARGE The body is larger than any operation takes: the API reads 2 MB of JSON at most.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-10-04. The response to POST /api/lab/threeds/authentications/4047e5c5-b8d6-4f1f-b971-1dc97734deb3/challenge. Arrays are cut to their first 4 items.

{
  "acsTransID": "4047e5c5-b8d6-4f1f-b971-1dc97734deb3",
  "phase": "AUTHENTICATED",
  "transStatus": "Y",
  "steps": [
    {
      "from": "Browser",
      "to": "ACS",
      "via": null,
      "message": "Challenge page",
      "fields": {
        "code": "845323"
      },
      "note": "The cardholder’s code, posted to the ACS’s page."
    },
    {
      "from": "ACS",
      "to": "3DS Server",
      "via": "Directory Server",
      "message": "RReq",
      "fields": {
        "messageType": "RReq",
        "messageVersion": "2.2.0",
        "threeDSServerTransID": "8a880dc0-d2d2-4067-bcb1-b08d1690b26e",
        "acsTransID": "4047e5c5-b8d6-4f1f-b971-1dc97734deb3",
        "dsTransID": "6224dbca-ce3e-4e0e-a3c3-5e2be56a98db",
        "messageCategory": "01",
        "authenticationType": "02",
        "interactionCounter": "01",
        "transStatus": "Y",
        "eci": "05",
        "authenticationValue": "VkF21zQ2iZjlS4gndDrGKNdZ4DM="
      },
      "note": "How the challenge ended, with the authentication value for the authorisation."
    },
    {
      "from": "3DS Server",
      "to": "ACS",
      "via": "Directory Server",
      "message": "RRes",
      "fields": {
        "messageType": "RRes",
        "messageVersion": "2.2.0",
        "threeDSServerTransID": "8a880dc0-d2d2-4067-bcb1-b08d1690b26e",
        "acsTransID": "4047e5c5-b8d6-4f1f-b971-1dc97734deb3",
        "dsTransID": "6224dbca-ce3e-4e0e-a3c3-5e2be56a98db",
        "resultsStatus": "01"
      },
      "note": "The 3DS Server has the results."
    },
    {
      "from": "ACS",
      "to": "3DS Server",
      "via": "Browser",
      "message": "CRes",
      "fields": {
        "messageType": "CRes",
        "messageVersion": "2.2.0",
        "threeDSServerTransID": "8a880dc0-d2d2-4067-bcb1-b08d1690b26e",
        "acsTransID": "4047e5c5-b8d6-4f1f-b971-1dc97734deb3",
        "challengeCompletionInd": "Y",
        "transStatus": "Y"
      },
      "note": "The last CRes, posted through the browser to the notificationURL: the cardholder is back at the merchant."
    }
  ],
  "rules": [],
  "challenge": null,
  "valueCheck": {
    "amount": "€750.00",
    "checks": true,
    "otherAmount": "€750.01",
    "checksForOther": false
  },
  "budget": [
    {
      "name": "The code: browser to ACS",
      "millis": 40,
      "measured": false
    },
    {
      "name": "The ACS: the code checked",
      "millis": 4.411184,
      "measured": true
    },
    {
      "name": "RReq: ACS to directory server",
      "millis": 40,
      "measured": false
    },
    {
      "name": "RReq: directory server to 3DS Server",
      "millis": 40,
      "measured": false
    }
  ]
}

Traces

What OpenTelemetry recorded while a lab run ran: its spans, kept in memory for a while.

A run's trace

GET/api/traces/{traceId}

Every span of a recent pool or ledger run, by the id its result carried: parent, start, duration, attributes. The server keeps the last 8 traces, in memory.

Answers with
RunTrace
Limit per address
30 requests a minute, policy traces
Operation id
getTrace

Parameters

traceIdpath, required
stringmatches ^[0-9a-f]{32}$
The trace's id, 32 hexadecimal digits

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • TRACE_ID_INVALID traceId breaks a constraint its schema gives.
404Not Found
  • TRACE_NOT_FOUND No trace with that id is kept: the server keeps the last runs' traces only.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-10-03. The response to GET /api/traces/5fb11f17c661cff72c3895fd7b917c95. Arrays are cut to their first 4 items.

{
  "traceId": "5fb11f17c661cff72c3895fd7b917c95",
  "durationMs": 13153.35,
  "spans": [
    {
      "id": "2da16a10d34079ee",
      "parentId": null,
      "name": "http get /api/lab/pool/stream",
      "startMs": 0,
      "durationMs": 13153.35,
      "attributes": {
        "exception": "none",
        "status": "200",
        "http.url": "/api/lab/pool/stream",
        "outcome": "SUCCESS",
        "method": "GET",
        "uri": "/api/lab/pool/stream"
      },
      "error": false
    },
    {
      "id": "b38c5e8f619bd311",
      "parentId": "2da16a10d34079ee",
      "name": "lab.pool.run",
      "startMs": 5.84,
      "durationMs": 13145.49,
      "attributes": {
        "lab.hold.ms": "50",
        "lab.policy": "THROTTLE",
        "lab.pool.size": "10",
        "lab.rate": "500"
      },
      "error": false
    },
    {
      "id": "325be140c1f47906",
      "parentId": "b38c5e8f619bd311",
      "name": "lab.pool.fill",
      "startMs": 6.24,
      "durationMs": 451.82,
      "attributes": {},
      "error": false
    },
    {
      "id": "00f28578c45720ce",
      "parentId": "b38c5e8f619bd311",
      "name": "lab.pool.request",
      "startMs": 460.12,
      "durationMs": 53.33,
      "attributes": {
        "request.outcome": "COMPLETED",
        "request.index": "0"
      },
      "error": false
    }
  ],
  "dropped": 0
}

Site

What the site's pages ask of it: a page view counted without cookies or anyone's address, an introduction taken, and a job posting checked against the record.

What the assistant can do here

GET/api/assistant

Whether an account ready for it could ask a question of its own now and, if not, why; how long a question may be and how many a conversation takes; and the examples, the prepared answers and the gate's demonstrations, which run on replies kept for them whatever the answer.

Limit per address
30 requests a minute, policy assistant-status
Operation id
getAssistantStatus

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-11-08. The response to GET /api/assistant. Arrays are cut to their first 4 items.

{
  "live": true,
  "off": null,
  "offWords": null,
  "questionCharacters": 500,
  "questions": 12,
  "sections": 69,
  "examples": [
    {
      "name": "who",
      "question": "Who is Armen?",
      "about": "A question the record answers, from where he works and the areas he goes deep in.",
      "source": "SCRIPTED",
      "kind": "PREPARED",
      "offers": []
    },
    {
      "name": "stack",
      "question": "What does he work with now?",
      "about": "A question the record answers, from his current role.",
      "source": "SCRIPTED",
      "kind": "PREPARED",
      "offers": []
    },
    {
      "name": "three-d-secure",
      "question": "What did he do on 3-D Secure?",
      "about": "A question the record answers, from his years at Worldline and the area of expertise they back.",
      "source": "SCRIPTED",
      "kind": "PREPARED",
      "offers": []
    },
    {
      "name": "experience",
      "question": "How many years of experience does he have?",
      "about": "A question the record answers, from his roles and when Java began for him.",
      "source": "SCRIPTED",
      "kind": "PREPARED",
      "offers": []
    }
  ]
}

Ask an example question

POST/api/assistant/examples/{name}/answers

Server-sent events: one of the example questions, answered on the reply kept for it through the same gate. No model is called, and nothing is spent. sentence for each sentence let through, with the sections of the site's record it cites, each with its title and address on the site; withheld for a sentence held back, with why: it named what nothing it cited mentions (and what), it cited nothing, it shared nothing with what it cited, or it spoke in the first person, as the owner; action for each thing the guide shows, in its place among the sentences: a page to open (show_page, with its path), something to point at (point_at), an experiment to run (run_experiment, with its preset and whether on the server), a tour to start (start_tour) or a next step to offer (offer), each with its label; then done, with the refusal and its words when there is no answer from the record, how many sentences were shown and held back, how many actions were taken, and who wrote the answer at what cost, its cache tokens and the time to its first word; error instead if the model fails on the way.

Answers with
server-sent events
Limit per address
20 requests a minute, policy assistant-examples
Operation id
streamAssistantExample

Parameters

namepath, required
stringmatches ^[a-z0-9-]{1,60}$
The example's name, from the status

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • NAME_INVALID name breaks a constraint its schema gives.
404Not Found
  • EXAMPLE_NOT_FOUND There is no example by that name.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
erroran event, once the stream has begun
  • INTERNAL_ERROR Something failed on the server's side.
A recorded stream

Recorded on 2026-11-08. The stream that answered POST /api/assistant/examples/leading/answers.

event:sentence
data:{"text":"At Picsart, Armen reviews code and mentors mid-level engineers.","citations":[{"id":"practice:reviews-and-mentoring","title":"How I work: Reviews and mentoring","url":"/#working"}]}

: 1 event left out

event:sentence
data:{"text":"He also takes part in CI/CD, observability with Datadog and ELK, and p99 latency and throughput tuning.","citations":[{"id":"practice:production","title":"How I work: Production","url":"/#working"}]}

event:done
data:{"refusal":null,"words":null,"shown":3,"withheld":0,"questionsLeft":null,"drafting":{"model":"scripted","source":"SCRIPTED","inputTokens":null,"outputTokens":null,"costUsd":null,"latencyMillis":10,"cacheReadTokens":null,"cacheWriteTokens":null,"firstTextMillis":null},"actions":0}

Find the prepared answer a question asks for

POST/api/assistant/match

A question typed without an account, matched to a prepared answer by the record's terms, the hiring topics and its words: the example's name to ask for, a refusal by rule (pay, private life) with its words, or neither, when only an account's question could answer it. No model is called, and nothing of the question is kept: what went unmatched is counted by the topics it names.

Limit per address
30 requests a minute, policy assistant-match
Operation id
matchAssistantQuestion

Body

application/jsonrequired
AssistantController.Typed

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • UNREADABLE_BODY The body is not the JSON the operation takes.
  • QUESTION_INVALID question breaks a constraint its schema gives.
413Content Too Large
  • CONTENT_TOO_LARGE The body is larger than any operation takes: the API reads 2 MB of JSON at most.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-11-08. The response to POST /api/assistant/match.

{
  "example": "kafka",
  "refusal": null,
  "words": null
}

A proof-of-work challenge for a fit check

GET/api/fit/challenge

A challenge by version 1 of ALTCHA's protocol, as for introductions but ten times the work, good for one fit check and ten minutes. Refused while a posting of one's own cannot run.

Answers with
Challenge
Limit per address
10 requests a minute, policy fit-check-challenges
Operation id
getFitCheckChallenge

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • FIT_CHECK_OFF A posting of one's own cannot be checked here now: the status says why.
  • BUDGET_SPENT Today's budget for this is spent. It starts again at midnight UTC.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-10-03. The response to GET /api/fit/challenge.

{
  "algorithm": "SHA-256",
  "challenge": "b1e68bc88fb30dd592efa96486346e486f7178c06d3a69d4076bc7b940bd2ef8",
  "maxnumber": 1000000,
  "salt": "b6dbb258ca6b9cd624863497?expires=1791068410&purpose=fit-check&",
  "signature": "c919b488b7dec71173429b88c2841c9a29ca371d03553598f28ba8e751156e52"
}

Check an example posting

POST/api/fit/examples/{name}/runs

One of the example postings, run end to end on the replies kept for it: no model is called, and nothing is spent.

Answers with
FitResult
Limit per address
20 requests a minute, policy fit-check-examples
Operation id
runFitCheckExample

Parameters

namepath, required
stringmatches ^[a-z0-9-]{1,60}$
The example's name, from the status

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • NAME_INVALID name breaks a constraint its schema gives.
404Not Found
  • EXAMPLE_NOT_FOUND There is no example by that name.
422Unprocessable Content
  • NO_REQUIREMENTS_FOUND The posting asks for nothing a candidate could show.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-11-08. The response to POST /api/fit/examples/payments-tech-lead/runs. Arrays are cut to their first 4 items.

{
  "requirements": [
    {
      "text": "7+ years of backend development in Java, including recent Spring Boot",
      "must": true,
      "rating": "DIRECT",
      "claim": "Java in production since 2017: Synopsys, then Worldline, then Picsart, whose services are Java 21 and Spring Boot.",
      "missing": "",
      "evidence": [
        {
          "title": "Picsart: Senior Software Engineer (Jan 2023–now)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Synopsys: Java Developer (Dec 2017–Jul 2019)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Where I work from, and how",
          "kind": "profile",
          "url": "/#contact"
        }
      ],
      "withheld": false
    },
    {
      "text": "Experience with card payments: 3-D Secure, PCI DSS, issuer or acquirer integrations",
      "must": true,
      "rating": "DIRECT",
      "claim": "Three and a half years on 3-D Secure 2.x at Worldline: integrations with acquirers and issuers, PCI DSS, and the edge cases of the critical transaction path.",
      "missing": "",
      "evidence": [
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Payment security",
          "kind": "expertise",
          "url": "/#expertise"
        }
      ],
      "withheld": false
    },
    {
      "text": "Production experience with PostgreSQL and Kafka",
      "must": true,
      "rating": "DIRECT",
      "claim": "Event-driven pipelines on Kafka in production at Picsart, and Kafka with PostgreSQL and Oracle at Worldline.",
      "missing": "",
      "evidence": [
        {
          "title": "Picsart: Senior Software Engineer (Jan 2023–now)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        }
      ],
      "withheld": false
    },
    {
      "text": "Kubernetes in production, on AWS or GCP",
      "must": true,
      "rating": "DIRECT",
      "claim": "Services on Kubernetes (GKE) at Picsart and on Kubernetes at Worldline; GCP and AWS are among the CV's cloud skills.",
      "missing": "AWS is not described in a role.",
      "evidence": [
        {
          "title": "Picsart: Senior Software Engineer (Jan 2023–now)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Skills on the CV: Cloud & delivery",
          "kind": "skills",
          "url": "/#experience"
        }
      ],
      "withheld": false
    }
  ],
  "withheld": 0,
  "drafting": {
    "model": "scripted",
    "source": "SCRIPTED",
    "calls": 2,
    "inputTokens": null,
    "outputTokens": null,
    "costUsd": null,
    "latencyMillis": 0
  }
}

What the fit check can do here

GET/api/fit/public

Whether a posting of your own can be checked now and, if not, why; how long it may be; and the examples, which run on replies kept for them whatever the answer.

Limit per address
30 requests a minute, policy fit-check-status
Operation id
getFitCheckStatus

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-11-08. The response to GET /api/fit/public. Arrays are cut to their first 4 items.

{
  "live": true,
  "off": null,
  "offWords": null,
  "postingCharacters": 6000,
  "examples": [
    {
      "name": "ai-backend",
      "company": "Example Commerce",
      "role": "Senior Backend Engineer, AI Features",
      "about": "AI on record is generative AI at Picsart and this site’s own assistant; LLM APIs at work, prompt injection, multi-tenant SaaS, Python and retrieval-augmented generation are not.",
      "source": "SCRIPTED"
    },
    {
      "name": "hidden-instruction",
      "company": "Example Robotics",
      "role": "Senior Java Developer, Robotics Backend",
      "about": "An attack: the posting tells the model to invent Rust and German, and the scripted reply obeys. The claim check catches every invention: a draft cannot be published until the owner fixes it, and a visitor's result withholds the claim.",
      "source": "SCRIPTED"
    },
    {
      "name": "munich-onsite",
      "company": "Example Bank",
      "role": "Java Developer, Munich",
      "about": "The stack fits; German at C2 and five days on site in Munich do not, and the site does not say whether he would relocate.",
      "source": "SCRIPTED"
    },
    {
      "name": "payments-tech-lead",
      "company": "Example Payments",
      "role": "Tech Lead, Payments Platform",
      "about": "A close fit on payments, Java and Kafka; mentoring is on record but leading a team is not, and the site says nothing of English or German.",
      "source": "SCRIPTED"
    }
  ]
}

Check a posting

POST/api/fit/runs

Reads the posting's requirements with a language model, shortlists the evidence on this site for each in code, has the model rate each against its shortlist only, and checks every claim against the evidence it cites. Nothing of the posting is kept, and the result has no link: it is yours alone. A claim the check found naming what its evidence does not is withheld.

Answers with
FitResult
Limit per address
3 requests an hour, policy fit-check-runs
Operation id
runFitCheck

Body

application/jsonrequired
PublicFitCheckController.Posting

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • UNREADABLE_BODY The body is not the JSON the operation takes.
  • POSTING_INVALID posting breaks a constraint its schema gives.
  • ALTCHA_INVALID altcha breaks a constraint its schema gives.
  • PROOF_OF_WORK_EXPIRED The challenge expired before it was used: solve a new one.
  • PROOF_OF_WORK_INVALID The proof of work does not solve a challenge this server issued for this, or was used already.
413Content Too Large
  • CONTENT_TOO_LARGE The body is larger than any operation takes: the API reads 2 MB of JSON at most.
422Unprocessable Content
  • NO_REQUIREMENTS_FOUND The posting asks for nothing a candidate could show.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
502Bad Gateway
  • MODEL_REFUSED The model declined to answer.
  • MODEL_REPLY_CUT_SHORT The model's reply was cut short.
  • MODEL_REPLY_MALFORMED The model's reply could not be read.
  • MODEL_REQUEST_REJECTED The model's provider rejected the request.
503Service Unavailable
  • FIT_CHECK_OFF A posting of one's own cannot be checked here now: the status says why.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
  • MODEL_RATE_LIMITED The model's provider is limiting requests; try again in a minute.
  • MODEL_OVERLOADED The model's provider is overloaded; try again shortly.
  • MODEL_UNAVAILABLE The model's provider cannot be reached.
  • BUDGET_SPENT Today's budget for this is spent. It starts again at midnight UTC.
  • MODEL_BUSY As many calls as this allows are running; try again in a minute.
A recorded response

Recorded on 2026-11-08. The response to POST /api/fit/examples/payments-tech-lead/runs. No visitor's posting is recorded: an example's run, which answers in the same shape, stands in for one. Arrays are cut to their first 4 items.

{
  "requirements": [
    {
      "text": "7+ years of backend development in Java, including recent Spring Boot",
      "must": true,
      "rating": "DIRECT",
      "claim": "Java in production since 2017: Synopsys, then Worldline, then Picsart, whose services are Java 21 and Spring Boot.",
      "missing": "",
      "evidence": [
        {
          "title": "Picsart: Senior Software Engineer (Jan 2023–now)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Synopsys: Java Developer (Dec 2017–Jul 2019)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Where I work from, and how",
          "kind": "profile",
          "url": "/#contact"
        }
      ],
      "withheld": false
    },
    {
      "text": "Experience with card payments: 3-D Secure, PCI DSS, issuer or acquirer integrations",
      "must": true,
      "rating": "DIRECT",
      "claim": "Three and a half years on 3-D Secure 2.x at Worldline: integrations with acquirers and issuers, PCI DSS, and the edge cases of the critical transaction path.",
      "missing": "",
      "evidence": [
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Payment security",
          "kind": "expertise",
          "url": "/#expertise"
        }
      ],
      "withheld": false
    },
    {
      "text": "Production experience with PostgreSQL and Kafka",
      "must": true,
      "rating": "DIRECT",
      "claim": "Event-driven pipelines on Kafka in production at Picsart, and Kafka with PostgreSQL and Oracle at Worldline.",
      "missing": "",
      "evidence": [
        {
          "title": "Picsart: Senior Software Engineer (Jan 2023–now)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        }
      ],
      "withheld": false
    },
    {
      "text": "Kubernetes in production, on AWS or GCP",
      "must": true,
      "rating": "DIRECT",
      "claim": "Services on Kubernetes (GKE) at Picsart and on Kubernetes at Worldline; GCP and AWS are among the CV's cloud skills.",
      "missing": "AWS is not described in a role.",
      "evidence": [
        {
          "title": "Picsart: Senior Software Engineer (Jan 2023–now)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Worldline: Java Developer, Payment Security (3-D Secure) (Jul 2019–Jan 2023)",
          "kind": "role",
          "url": "/#experience"
        },
        {
          "title": "Skills on the CV: Cloud & delivery",
          "kind": "skills",
          "url": "/#experience"
        }
      ],
      "withheld": false
    }
  ],
  "withheld": 0,
  "drafting": {
    "model": "scripted",
    "source": "SCRIPTED",
    "calls": 2,
    "inputTokens": null,
    "outputTokens": null,
    "costUsd": null,
    "latencyMillis": 0
  }
}

Say that a tour started or finished

POST/api/guide/events

Tours play in the browser without a model, so the page says when one starts and when one ends, by the tour's id, and the guide's daily counts take it; nothing else in the body is read. A request with Sec-GPC: 1 or DNT: 1, or one another site's page sent, is answered alike and not counted.

Answers with
no content, 204
Limit per address
30 requests a minute, policy guide-events
Operation id
recordGuideEvent

Body

application/jsonrequired
GuideEventController.Event

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • UNREADABLE_BODY The body is not the JSON the operation takes.
  • EVENT_INVALID event breaks a constraint its schema gives.
  • TOUR_INVALID tour breaks a constraint its schema gives.
  • TOUR_UNKNOWN There is no tour by that id in the site's manifest.
413Content Too Large
  • CONTENT_TOO_LARGE The body is larger than any operation takes: the API reads 2 MB of JSON at most.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.

Draft an introduction

POST/api/introductions

Who you are and what you want to say, with a solved challenge in altcha, and, if you choose, your conversation with the site's guide in conversation, as text. The draft comes back as it will be kept, with the token that confirms it; it waits 30 minutes for that. Leave website out: it is there for programs that fill in every field.

Answers with
Preview
Limit per address
10 requests an hour, policy introductions
Operation id
draftIntroduction

Body

application/jsonrequired
Draft

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • UNREADABLE_BODY The body is not the JSON the operation takes.
  • NAME_INVALID name breaks a constraint its schema gives.
  • EMAIL_INVALID email breaks a constraint its schema gives.
  • COMPANY_INVALID company breaks a constraint its schema gives.
  • ROLE_INVALID role breaks a constraint its schema gives.
  • MESSAGE_INVALID message breaks a constraint its schema gives.
  • CONVERSATION_INVALID conversation breaks a constraint its schema gives.
  • ALTCHA_INVALID altcha breaks a constraint its schema gives.
  • TOO_MANY_LINKS The message carries more links than a few words to a person would.
  • CONVERSATION_TOO_MANY_LINKS The conversation sent with the introduction carries more links than a conversation with the guide would.
  • PROOF_OF_WORK_EXPIRED The challenge expired before it was used: solve a new one.
  • PROOF_OF_WORK_INVALID The proof of work does not solve a challenge this server issued for this, or was used already.
413Content Too Large
  • CONTENT_TOO_LARGE The body is larger than any operation takes: the API reads 2 MB of JSON at most.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_NOT_MIGRATED The database lacks the experiment's tables: its migrations have not run.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.
A recorded response

Recorded on 2026-10-03. The response to POST /api/introductions.

{
  "id": "08804f3c-ec62-450d-81b2-3801ea63419a",
  "name": "Alex Example",
  "email": "alex@example.com",
  "company": "Example GmbH",
  "role": "Engineering manager",
  "message": "Hello! We are hiring a Java tech lead in Berlin, remote first. Would you like to talk next week?",
  "token": "cahlfxIXq4_6oQB3Y7VPmndLRC6logBg3rXpupffM7w",
  "confirmBy": "2026-10-03T18:35:01.707971423Z"
}

A proof-of-work challenge

GET/api/introductions/challenge

A challenge by version 1 of ALTCHA's protocol, to solve before sending a draft: find the number from 0 to maxnumber whose SHA-256, after the salt, is the challenge. Good for ten minutes and for one draft.

Answers with
Challenge
Limit per address
20 requests a minute, policy introduction-challenges
Operation id
getIntroductionChallenge

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
A recorded response

Recorded on 2026-10-03. The response to GET /api/introductions/challenge.

{
  "algorithm": "SHA-256",
  "challenge": "7e2c8fc8b6d775d8e0b87adcdd3fccf3762b38d243ea9baccb36b4e55711ba5e",
  "maxnumber": 100000,
  "salt": "d935dc0d911fea7a18d0f41d?expires=1791061866&purpose=introduction&",
  "signature": "126bae74a27bdf4cf812b4a26b237365098e79ed4bc23893f249736bc3d5c6a5"
}

Confirm an introduction

POST/api/introductions/{id}/confirmation

Sends the draft on, with the token it came back with. Confirming twice is confirming once.

Answers with
no content, 204
Limit per address
10 requests an hour, policy introductions
Operation id
confirmIntroduction

Parameters

idpath, required
string
The draft's id

Body

application/jsonrequired
Confirmation

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • UNREADABLE_BODY The body is not the JSON the operation takes.
  • TOKEN_INVALID token breaks a constraint its schema gives.
404Not Found
  • INTRODUCTION_NOT_FOUND No draft with that id and token is waiting.
409Conflict
  • INTRODUCTION_EXPIRED The draft was not confirmed in time.
413Content Too Large
  • CONTENT_TOO_LARGE The body is larger than any operation takes: the API reads 2 MB of JSON at most.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_NOT_MIGRATED The database lacks the experiment's tables: its migrations have not run.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.

Count a page view

POST/api/visits

The beacon the site's pages send as they are shown: the path, the tag of a tagged link, the host that referred the visitor and the size of screen, with the day's pseudonym of the visitor, an HMAC of address and user agent under a salt that lasts a day. Neither is stored. A request with Sec-GPC: 1 or DNT: 1, one a browser says another site's page sent (Sec-Fetch-Site), or one from a user agent that says it is a program, is answered the same and leaves nothing.

Answers with
no content, 204
Limit per address
60 requests a minute, policy visits
Operation id
recordVisit

Parameters

pathquery, required
stringmatches ^/[A-Za-z0-9/._~-]{0,199}$
The page's path, without query or fragment
tagquery
stringmatches ^[a-z0-9][a-z0-9-]{0,39}$
The tag of the link the visitor came by: acme, from ?r=acme
referrerquery
stringmatches ^[a-z0-9.-]{1,253}$
The host of the page that linked here, never its path
viewportquery, required
NARROW, MEDIUM or WIDE
The size of screen: narrower than 640 CSS pixels, narrower than 1,024, or wider

Errors

400Bad Request
  • UNSUPPORTED_API_VERSION API-Version names a version the API does not have.
  • MISSING_PARAMETER A required parameter is missing.
  • INVALID_PARAMETER A parameter does not parse: not a number, or not one of its values.
  • PATH_INVALID path breaks a constraint its schema gives.
  • TAG_INVALID tag breaks a constraint its schema gives.
  • REFERRER_INVALID referrer breaks a constraint its schema gives.
429Too Many Requests
  • RATE_LIMITED More requests from this address than the operation's limit allows.
500Internal Server Error
  • INTERNAL_ERROR Something failed on the server's side.
503Service Unavailable
  • DATABASE_NOT_CONFIGURED The server runs without a database.
  • DATABASE_NOT_MIGRATED The database lacks the experiment's tables: its migrations have not run.
  • DATABASE_UNAVAILABLE The database cannot be reached, or cannot give connections in time.

Schemas

Every field is always present; the ones that can be null say so.

AlgorithmResult

algorithm
NAIVE_RECURSION, MEMOIZED_RECURSION, ITERATIVE, MATRIX_POWER or FAST_DOUBLING
label
string
complexity
string
status
OK, SKIPPED, STACK_OVERFLOW or TIMEOUT
nanos
integer or null
runs
integer
operations
integer or null
maxDepth
integer or null
note
string or null

AssistantController.Typed

question
string

Challenge

algorithm
string
challenge
string
maxnumber
integer
salt
string
signature
string

Confirmation

token
string

DatabaseInfo

version
string
migrations
array of DatabaseInfo.Migration

DatabaseInfo.Migration

version
string
description
string
installedOn
date-time
success
boolean

Draft

name
string
email
string
company
string or null
role
string or null
message
string
conversation
string or null
website
string or null
altcha
string

FibonacciReport

n
integer
digits
integer
leadingDigits
string
trailingDigits
string
value
string or null
results
array of AlgorithmResult

FibonacciReport.Environment

javaVersion
string
availableProcessors
integer

FitChecks.ExampleSummary

name
string
company
string
role
string
about
string
source
LIVE, RECORDED or SCRIPTED

FitPage.Cited

title
string
kind
string
url
string

FitPage.Drafting

model
string
source
LIVE, RECORDED or SCRIPTED
calls
integer
inputTokens
integer or null
outputTokens
integer or null
costUsd
number or null
latencyMillis
integer

FitPage.Line

text
string
must
boolean
rating
DIRECT, ADJACENT or NONE
claim
string
missing
string
evidence
array of FitPage.Cited
withheld
boolean

FitResult

requirements
array of FitPage.Line
withheld
integer

GuideEventController.Event

event
string
tour
string

Invariant.Verdict

name
string
holds
boolean
detail
string

JitReport

completed
boolean
callsPerBatch
integer
batches
array of JitReport.Batch
events
array of JitReport.Event

JitReport.Batch

index
integer
shape
MONOMORPHIC, BIMORPHIC or MEGAMORPHIC
nanos
integer

JitReport.Event

batch
integer
kind
COMPILED or DEOPTIMIZED
method
string
detail
string

JitReport.Summary

firstNanos
integer
monomorphicNanos
integer
bimorphicNanos
integer
megamorphicNanos
integer

JvmFacts

collector
string
aotCache
string or null
aotCacheInUse
boolean
compactObjectHeaders
boolean or null
flags
array of string
memoryLimitBytes
integer

LedgerReport

strategy
READ_MODIFY_WRITE, GUARDED_UPDATE, LOCK_UNORDERED, LOCK_ORDERED, OPTIMISTIC or SERIALIZABLE
hotSpotPercent
integer
transfers
integer
workers
integer
committed
integer
insufficientFunds
integer
gaveUp
integer
failed
integer
notStarted
integer
deadlocks
integer
serializationFailures
integer
conflicts
integer
elapsedMs
integer
throughput
number
latencyP50Ms
integer or null
latencyP99Ms
integer or null
invariants
array of Invariant.Verdict
reconciliation
Reconciliation
traceId
string or null

OutboxReport

design
COMMIT_THEN_PUBLISH, PUBLISH_THEN_COMMIT or OUTBOX
crashPercent
integer
dedup
boolean
orders
integer
answered
integer
committed
integer
published
integer
duplicatePublishes
integer
lost
integer
phantoms
integer
deliveries
integer
effects
integer
duplicateEffects
integer
missingEffects
integer
effectsWithoutOrder
integer
crashes
map of integer
restarts
integer
elapsedMs
integer
verdicts
array of OutboxReport.Verdict

OutboxReport.Verdict

name
string
holds
boolean
detail
string

PlanSummary

access
EVERY_ROW or INDEX
index
string or null
workers
integer
rowsRead
integer
blocksHit
integer
blocksRead
integer
planningMs
number
executionMs
number
steps
array of PlanSummary.Step

PlanSummary.Step

depth
integer
node
string
on
string or null
rows
integer
loops
integer

Preview

id
string
name
string
email
string
company
string or null
role
string or null
message
string
conversation
string or null
token
string
confirmBy
date-time

ProfileReport

scenario
HOT_LOCK, ALLOCATION_CHURN or QUADRATIC_DEDUPE
variant
PROBLEM or FIX
completed
boolean
wallMs
integer
threads
integer
checksum
string
samples
integer
hottest
array of ProfileReport.HotFrame

ProfileReport.AllocatedClass

type
string
bytes
integer
share
number

ProfileReport.Allocations

sampledBytes
integer

ProfileReport.Frame

name
string
total
integer
self
integer
children
array of ProfileReport.Frame

ProfileReport.Gc

collections
integer
pauseTotalMs
integer
longestPauseMs
integer

ProfileReport.HotFrame

frame
string
self
integer
share
number

ProfileReport.Locks

waits
integer
totalMs
integer
monitor
string or null

PublicAssistant.ExampleSummary

name
string
question
string
about
string
source
LIVE, RECORDED or SCRIPTED or null
kind
PREPARED or DEMO
offers
array of string

PublicAssistant.Matched

example
string or null
refusal
PAY, PRIVATE_LIFE, NOT_IN_RECORD or UNGROUNDED or null
words
string or null

PublicAssistant.Status

live
boolean
off
OFF, NO_MODEL, NO_PRICE, NO_DATABASE or BUDGET_SPENT or null
offWords
string or null
questionCharacters
integer
questions
integer
sections
integer

PublicFitCheckController.Posting

posting
string
altcha
string

PublicFitChecks.Status

live
boolean
off
OFF, NO_MODEL, NO_PRICE, NO_DATABASE or BUDGET_SPENT or null
offWords
string or null
postingCharacters
integer
examples
array of FitChecks.ExampleSummary

Reconciliation

accountsDrifted
integer
moneyCreated
integer
moneyDestroyed
integer
worst
array of Reconciliation.Drift

Reconciliation.Drift

account
integer
cached
integer
fromEntries
integer

RiskEngine.Asked

rule
string
decided
boolean
words
string

RunTrace

traceId
string
durationMs
number
spans
array of RunTrace.Span
dropped
integer

RunTrace.Span

id
string
parentId
string or null
name
string
startMs
number
durationMs
number
attributes
map of string
error
boolean

RuntimeInfo

javaVersion
string
javaVendor
string
vm
string
springBootVersion
string
availableProcessors
integer
maxHeapBytes
integer
garbageCollectors
array of string
startedAt
date-time
uptimeMs
integer
revision
string or null

SearchReport

query
string
methods
array of SearchReport.Method

SearchReport.Catalogue

products
integer
bytes
integer

SearchReport.Method

method
SCAN, TRIGRAM, TYPO or FULL_TEXT
outcome
COMPLETED or TIMED_OUT
total
integer
plan
PlanSummary or null

SearchReport.Product

id
integer
title
string
category
string
priceCents
integer
score
number or null

TelemetrySample

t
integer
uptimeMs
integer
nonHeapUsed
integer
classesLoaded
integer

TelemetrySample.Cpu

process
number
system
number

TelemetrySample.Gc

name
string
count
integer
timeMs
integer

TelemetrySample.Heap

used
integer
committed
integer
max
integer

TelemetrySample.Threads

live
integer
daemon
integer
peak
integer

ThreadLocalReport

tasks
integer
poolSize
integer
carrierThreads
integer

ThreadLocalReport.Cell

source
THREAD_LOCAL, PER_CALL or SHARED
executor
PLATFORM or VIRTUAL
formattersCreated
integer
allocatedBytesPerTask
integer or null
wallMicros
integer

ThreeDsLabController.Answer

code
string

ThreeDsRun

acsTransID
string
phase
INVALID, FRICTIONLESS, CHALLENGED, AUTHENTICATED, FAILED, NOT_AUTHENTICATED or REJECTED
transStatus
string or null
steps
array of ThreeDsRun.Step
rules
array of RiskEngine.Asked
challenge
ThreeDsRun.Challenge or null
valueCheck
ThreeDsRun.ValueCheck or null
budget
array of ThreeDsRun.Hop

ThreeDsRun.Challenge

sentTo
string
code
string
triesLeft
integer

ThreeDsRun.Hop

name
string
millis
number
measured
boolean

ThreeDsRun.Step

from
string
to
string
via
string or null
message
string
fields
map of string
note
string or null

ThreeDsRun.ValueCheck

amount
string
checks
boolean
otherAmount
string
checksForOther
boolean

Generated, not written: a test builds this document from the running application with springdoc, adds the errors, limits and versions the code enforces and responses recorded from the API, and fails the build while this copy differs from it. Why it is made that way.

How the service runs