Define a capability
Describe one machine-callable service with schemas, fulfillment, pricing, and operation modes.
A capability is what a buyer understands: a description, input contract,
output contract, price, and fulfillment behavior. EntrypointDef is the SDK
object that represents it.
Define the public contract
runtime.entrypoints.add({
key: 'classify',
description: 'Classify text into one supported category',
input: z.object({
text: z.string().min(1),
categories: z.array(z.string().min(1)).min(2),
}),
output: z.object({
category: z.string(),
confidence: z.number().min(0).max(1),
}),
price: '0.02',
handler: async ({ input, signal, runId }) => {
const result = await classify(input, { signal, runId });
return { output: result };
},
});Contract rules
keyis stable API identity. Changing it changes URLs, task targets, and discovery metadata.- Validate both input and output. A paid response with the wrong shape is a fulfillment failure.
- Make cancellation observable through
signal. - Use
runIdfor logs; use an idempotency key for business deduplication. - Set
paymentProtocolwhen both x402 and MPP are installed. - Keep framework objects out of the handler. Request headers belong in typed metadata supplied by the HTTP extension.
The invoke route accepts either the input value directly or the canonical
{ "input": ... } envelope. A successful response contains run_id,
status: "succeeded", the validated output, and optional usage/model.
Treat that wire shape as public even when a generated storefront hides it.
Choose price and operation modes
price is a USD decimal string, not a token atomic amount or JavaScript
number. A single string applies to the capability's priced operation; use an
object when invoke and stream differ:
price: { invoke: '0.02', stream: '0.03' }When both x402 and MPP extensions are installed, set paymentProtocol
explicitly. An entrypoint-level network overrides the payments runtime
network for that capability. Do not advertise a stream price without a stream
handler or a task capability without a2a().
Bound fulfillment
Schema validation is necessary but not sufficient. Put explicit limits on
input bytes, array sizes, remote fetches, model/tool iterations, duration,
concurrency, and output size. Propagate signal to every downstream operation
and stop work when the caller disconnects or the task is cancelled.
Use runId for correlation. Use a stable business idempotency key and a
database uniqueness constraint for effects that must not repeat; a run ID is
new for each execution and is not a deduplication key.
Failure contract
| Failure | HTTP behavior | What to fix |
|---|---|---|
| Invalid JSON/outer request | 400 invalid_request | Client envelope |
| Input schema failure | 400 invalid_input | Client fields/limits |
| Output schema failure | 500 invalid_output | Handler implementation |
| Handler throw | 500 internal_error | Application/dependency; inspect payment state before retry |
| Missing handler | 501 not_implemented | Capability registration |
Input/output errors can occur inside the payment transaction. Read Payment lifecycle before promising whether a specific failure is charged or refundable.
Verify the capability
- Unit-test the handler with minimum, maximum, invalid, cancelled, and downstream-failure inputs.
- Invoke the free route and assert the exact response schema.
- Add a testnet price and assert an unpaid request returns the expected
402without running the handler. - Complete one paid request and reconcile schema-valid output plus settlement evidence.
- Retry the same operation concurrently with one idempotency key and assert the business effect occurs once.
The adapter-level addEntrypoint helper remains a compatibility delegate. New
runtime-oriented code should register through the canonical registry so the
same definition serves every adapter.
Next: receive x402, choose streaming or asynchronous tasks, and consult the core package reference.