Skip to content

Readable Interface: Enums, Parameter Names & Handler Phases

Design the schema for the model that calls it, not for the API behind it. An upstream API often speaks in terse codes; the language model should not have to decode them. Translate at the edge so the surface the model sees is self-explaining.


Say a fictional AcmeWeather API takes ?step=h1|d1|w1. Those codes mean nothing to a model. Expose readable values and map them to the wire format in preRequest:

// What the model sees — clear, guessable values:
parameters: [ {
position: { key: 'interval', value: '{{USER_PARAM}}', location: 'query' },
z: { primitive: 'enum(1-hour,1-day,1-week)', options: [] }
} ]
// preRequest translates to the API's codes (flat payload access):
preRequest: async ( { struct, payload } ) => {
const wire = { '1-hour': 'h1', '1-day': 'd1', '1-week': 'w1' }
struct.url = struct.url.replace( `interval=${ payload.interval }`, `step=${ wire[ payload.interval ] }` )
return { struct }
}

The caller picks a value it understands; the handler does the translation. The API contract is unchanged — only the surface improves.

The same idea applies to opaque keys in a response. A fictional AcmeMarkets candles endpoint returns { o, h, l, c, v, t }. Rename them to descriptive fields in postRequest so downstream tools get a stable, obvious shape:

postRequest: async ( { response } ) => {
const candles = response.data.map( ( { o, h, l, c, v, t } ) => ( {
open: o, high: h, low: l, close: c, volume: v, time: t
} ) )
return { response: { candles } }
}

A model can chart candles[].close; it cannot reliably guess what c means.

The two snippets above read the payload differently on purpose. Handlers run in three phases, and each sees the payload in one shape only:

PhaseWhenReads the payload as
preRequestbefore the callflatpayload.interval
executeRequestthe call itselfnestedpayload.userParams.interval
postRequestafter the callflatpayload.interval

Reading the wrong shape raises no error — the handler just never fires. So when a preRequest rewrite seems to do nothing, first check it reads payload.interval (flat), not payload.userParams.interval. preRequest is the canonical place for enum and key rewrites because it runs before the call with flat access to every parameter.