Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: External services

External services

Wire encodings, decoders and retries

This is the detail behind Decoding and Calling: the encodings a provider's fields can be sent in, a package's own decoders, which fields are decoded, and how retry: and timed_out: work.

  • The built-in encodings are ten, by these names: unix_seconds and unix_milliseconds (an Instant as a whole number), iso_8601 (an Instant with any offset from UTC, which the contract refuses, since it writes only Z), day_month_year (a CalendarDate, the encoding Jobs and events and the Standard library name for file rows, here built for providers), text (an Int or a Decimal as text), comma_separated (a list of single values), base64 (Text as the Base64 of its UTF-8), json_text (any value's JSON in a string), minor_units (Money in the one currency its type's currency: fixes, which Wire.minor_units needs) and number (an Int or a Decimal as a JSON number, its digits kept as written both ways). The contract writes a Decimal as text and reads no number for one, so without number a provider sending 2.50 could not be read at all, not even by a package's own decoder.
  • Where they apply. On the provider's wire only: a call's input, its answer and a webhook's event. json.write, a route's answer and an event's payload write the field as the contract writes its type, so an app's clients never see a provider's encoding, where a field's wire_name: is its spelling everywhere. In the plan a field that says how it is sent has a codec of its own, holding its type's contract codec and how the provider sends it.
  • What each refuses. wire: holds Wire.<name> naming one of the ten, on a field of a type that encoding holds; outside a provider package, beside decode: or encode:, or naming anything else, it is package.wire_invalid. A value the provider sends that its encoding can't read is a fault naming the operation, the field and the rule (type, format, max for an instant past the years an Instant holds), and a list item holding a comma, which comma_separated would split, is the fault comma_separated when the call is sent.
  • A field's own functions. decode: and encode: come together, each library.function in the package's own libraries/, internal or public. decode: takes one value, the field as the provider sends it, and gives the field's type; it may fail, which is a fault. encode: takes the field's type and gives what decode: takes, and may not fail. Either named but not the package's, or of another shape, is package.wire_invalid; they count as the functions' use. That the pair round-trips, and that they read every recording, is checked under "Recordings".
  • An operation's whole decoder. wire_output: and decode: come together, with output: (package.operation_invalid otherwise): decode: takes the wire_output: type and gives the output: type. The answer is read from output_at: as wire_output:, its own fields' encodings applied, and given whole to decode:.
  • Run at the call's site. The runtime asks the executor to run each package function where the call is made; a field decoder that fails is the problem decode on that field, and an operation's decoder that fails names how it ended (its failure's code or its fault's classification), never a value.
  • Only the fields read. Worked out per call site from its action's body: when the call's value, or its attempt's, is a variable's and every use of that variable is a field read, only those top-level fields are decoded and checked; the others hold none, which no code can read. Fields inside a read field are decoded with it. Rules across the type's fields are checked only when every field is read. An operation decoded whole is read whole.
  • Numbers as written. An answer is parsed with Jadpo's own reader, so a number keeps the digits the provider wrote and an answer naming a member twice isn't JSON; a request's body and query are written with each number's digits as the input's codec wrote them.
  • retry: takes attempts:, 1 to 10, and within:, a duration up to 5 minutes, each defaulting to 3 and 30 seconds; connection.value_invalid otherwise. A call is sent again when it never left or the provider said it did nothing (408, 429, its in_use: code), whatever it changes, and when it may have acted (a server's error, no whole answer, no answer in time) only if it changes nothing or takes its key, under the same key. Waits are 200 milliseconds doubled before each further attempt, drawn evenly from zero (full jitter, from the host's randomness), or a 408 or 429's Retry-After in seconds; a wait past within: stops the retrying. A host with no timer, as a test's, sends again at once and measures nothing. Each attempt goes through the capability seam and is logged, recorded and replayed as a call of its own.
  • timed_out: The driver tells an answer that didn't come within timeout: apart from other missing answers. Where the call's attempt maps timed_out: and the operation is safe to send again, the last attempt's timeout is the runtime's timed_out failure (kind timed_out, 504), mapped to the app's failure with it as the cause; anywhere else it is unavailable or outcome_unknown, as "How provider calls are made" says. timed_out: on an operation that changes something without a key is failure.unknown_mapping, to a failure that isn't unavailable failure.mapping_kind; an operation listing no failures may still be attempted for timed_out: alone.
  • What reviewers see. The package's surface writes an operation's wire_output X decode Y, so the answer as sent shows beside its output, and jadpo package show gives it as "sent as"; the manifest gives a field's wire.

Markdown for agents: /docs/wire-encodings-decoders-and-retries.md.Built from Jadpo main at a707efb.