Sonde

JSON result schema

JSON result schema shared by --json and --report-json

sonde [options] FILE... --json prints one line of this shape per file run; sonde --test --report-json DIR writes the same shape, accumulated across every invocation, to DIR/report.json. One schema serves both: see architecture.md §5, "JSON result contract".

The base fields below are byte-compatible with Hurl 8.0.1's own --json export — a Hurl parser or dashboard built against that schema reads a Sonde result unchanged. A sonde key, on a result or on an entry, holds Sonde-only data that has no Hurl equivalent (the data row of a --data run, the contract findings of --openapi, the messages of a streamed entry, the status of a gRPC call); it is additive within a major version and absent when empty.

Every string value in a result — a URL, header, cookie, capture, assert message, curl command, and so on — has already been redacted with the run's final secret union (internal/redact) and, for a --data run, with the row's secrets: a secret can never appear in a report in the clear.

Result (one file)

FieldTypeDescription
filenamestringThe file path as given on the command line.
successboolWhether every attempt that was not retried passed.
timeintegerTotal duration, in milliseconds.
cookiesCookie[]The cookie jar at the end of the run.
entriesEntry[]One object per attempt: a retried entry appears once per attempt, a repeated entry once per repetition.
sondeobject, optionalSonde-only data, absent when there is none. sonde.iteration.row (integer, 1-based) is the data row of a --data run; filename stays the file path.
FieldType
domainstring
expiresinteger (Unix seconds, 0 when session-only)
httpsbool
include_subdomainbool
namestring
pathstring
valuestring

Entry (one attempt)

FieldTypeDescription
indexinteger1-based entry index in the file.
lineintegerSource line of the request method.
timeintegerAttempt duration, in milliseconds.
curl_cmdstringThe equivalent curl command line.
callsCall[]One HTTP exchange per call; a redirect produces more than one.
capturesCapture[]Variables captured by this attempt.
assertsAssert[]One per implicit or explicit assert, in source order; with --openapi, each contract violation adds a failed assert at the status line.
sondeobject, optionalSonde-only data, absent when there is none. sonde.contract.violations (Violation[]) lists the contract findings of the attempt's final response (--openapi); absent when it conforms. sonde.stream (Stream) holds the messages of a streamed entry (Server-Sent Events, WebSocket or a server-streaming gRPC call, .sonde files only). sonde.grpc (GRPCStatus) is the status of a gRPC call; absent for other entries and for a gRPC stream stopped before its status.

Violation

FieldTypePresent when
kindstringalways: unmatched, status, header, content-type, body, or error (the response could not be checked)
messagestringalways
operationstringan operation matched, e.g. "GET /pets/{petId}"
spec_pointerstringthe rule is located in the spec: a JSON pointer fragment, e.g. "#/paths/~1pets/get/responses/200"
instance_pathstringthe finding is located in the response: a JSON pointer into the body ("/0/name") or "header <Name>"
warningbool (true or omitted)the finding does not fail the entry (a request no operation matches, without --openapi-strict)

Stream

What a streamed entry exchanged after its response headers (guides/streaming.md).

FieldTypeDescription
protocolstringsse, websocket or grpc.
stop_reasonstring, optionalWhy the stream ended: count, timeout, max-bytes, closed (by the server) or script (every WebSocket step ran). Absent when the stream failed.
sentintegerMessages sent (WebSocket).
receivedintegerEvents or messages received.
messagesStreamMessage[]Every message, in order. They are written inline, unlike bodies; sonde-stream-max-bytes bounds their size.

StreamMessage

FieldTypePresent when
directionstringalways: sent or received
datastringalways: the text, or base64 when binary is set (redacted before encoding)
binarybool (true or omitted)a WebSocket binary message
eventstringan SSE event (message when the event names none)
idstringan SSE event after an id field
retryintegeran SSE event with a valid retry field
timeintegeralways: milliseconds since the response headers

A gRPC reply's data is its JSON (guides/grpc.md).

GRPCStatus

The status of a gRPC call (guides/grpc.md).

FieldTypeDescription
codeintegerThe status code: 0 is OK.
statusstringThe code's name: OK, NOT_FOUND…; UNKNOWN for a code gRPC does not define.
messagestringThe status message, percent-decoded; empty when none. Redacted.

Call

FieldType
requestRequest
responseResponse
timingsTimings

Request

FieldType
methodstring
urlstring
headersNameValue[]
cookiesNameValue[] (the request's own Cookie header, split into pairs)
query_stringNameValue[] (the URL's query parameters, decoded)

Response

FieldTypeDescription
http_versionstring"HTTP/1.0", "HTTP/1.1", "HTTP/2" or "HTTP/3".
statusinteger
headersNameValue[]
cookiesResponseCookie[]Every Set-Cookie header, parsed.
bodystring, omitted for --jsonPresent only in a --report-json report: the response body's path, relative to report.json (see "Response bodies" below).
certificateCertificate, optionalThe server certificate of an HTTPS response.

Certificate

FieldTypeDescription
expire_datestring"2028-03-16 05:18:48 UTC"
issuerstringe.g. "C = US, O = Example, CN = Example CA"
serial_numberstringlowercase hex bytes separated by :
start_datestringsame form as expire_date
subjectstringsame form as issuer
subject_alt_namestringe.g. "DNS:localhost, IP Address:127.0.0.1"
valuestringthe certificate in PEM format

ResponseCookie

FieldTypePresent when
namestringalways
valuestringalways
domainstringthe Domain attribute was set
pathstringthe Path attribute was set
expiresstringthe Expires attribute was set (raw text)
max_agestringthe Max-Age attribute was set (raw text, not re-parsed)
same_sitestringthe SameSite attribute was set
securebool (true or omitted)the Secure flag was set
httponlybool (true or omitted)the HttpOnly flag was set

NameValue

FieldType
namestring
valuestring

Timings

All fields are integer milliseconds, except the two timestamps, which are UTC in 2006-01-02T15:04:05.000000Z form.

Field
name_lookup
connect
app_connect
pre_transfer
start_transfer
total
begin_call (timestamp)
end_call (timestamp)

Capture

FieldType
namestring
valuesee "Capture value encoding" below

Assert

FieldTypePresent when
lineintegeralways
successboolalways
messagestringsuccess is false: the rendered failure, source snippet included

Capture value encoding

A captured value is encoded in the JSON type closest to it:

Captured kindJSON encoding
nullnull
booltrue / false
integer that fits in 64 bitsnumber
integer too large for thatnumber, unquoted (arbitrary precision)
floatnumber
stringstring
bytesbase64 string
dateits display text (e.g. 2024-01-01T00:00:00Z)
regexits source pattern, as a string
listarray, each element encoded the same way
object{"key": value, ...}, member order preserved
XPath nodeset{"type": "nodeset", "size": <count>}
unit (a query that matched but produced no data, e.g. a cookie flag){"type": "unit"}
one hop of a redirect chain{"status": <code>, "location": <url or "None">}

Response bodies (--report-json only)

--json never embeds or references a response body. --report-json DIR saves each call's response body under DIR/store/ and points body at it:

DIR/
├── report.json
└── store/
    ├── 5b1f2c3e-<uuid>-b6b1e9b4a5df_response.json
    ├── 8b53a2c1-<uuid>-2c7a2edc0a63_response.html
    └── ...

The file is named <random-id>_response, with an extension picked from the response's Content-Type (.json, .xml, .html, or none); body holds the path relative to report.json, e.g. "store/5b1f2c3e-<uuid>-b6b1e9b4a5df_response.json".

Cumulative reports

Every --report-* flag accumulates: running sonde --test --report-json DIR ... twice appends the second run's files to the first's in report.json (and its response bodies to store/), rather than overwriting it. --report-junit and --report-tap behave the same way; see docs/architecture.md for their formats.

Edit on GitHubLast updated