expresses
expresses
Declare how a machine is reachable by the outside world. The expresses section defines the surfaces through which users, other machines, and external systems can interact with the machine. Every surface inherits the machine’s governance: a machine that is not allowed to access the network cannot be reached via an API surface that bypasses those restrictions.
MCP (Model Context Protocol) is implicit: every machine is automatically available as an MCP tool. The expresses section is for declaring additional surfaces beyond MCP.
When to use
Use expresses when a machine needs to be reachable through:
- A REST API endpoint
- A WebSocket connection for real-time communication
- A webhook receiver for external event sources (Stripe, GitHub, etc.)
- A web page served to end users (Kanvas)
- An A2A (Agent-to-Agent) protocol endpoint
Omit expresses for machines that are only called by other machines via ask ... from or that only respond to events via subscribes. MCP availability does not require an expresses declaration.
Syntax
expresses <surface_type> <configuration>Multiple surfaces can be declared in a single expresses section.
Surface types
api
Expose the machine as a REST API endpoint.
expresses api path: "/api/classify" method: "POST" authentication: "bearer_token"| Config | Required | Description |
|---|---|---|
path | Yes | URL path for the endpoint |
method | No | HTTP method: "GET", "POST", "PUT", "DELETE" (default: "POST") |
authentication | No | Auth type: "bearer_token", "api_key", "none" (default: "none") |
webhook
Receive incoming webhooks from external services.
expresses webhook path: "/hooks/stripe" authentication: "hmac_sha256" provider: "stripe"| Config | Required | Description |
|---|---|---|
path | Yes | URL path for the webhook receiver |
authentication | No | Signature verification method |
provider | No | Named provider for pre-configured signature verification |
websocket
Establish a WebSocket connection for bidirectional real-time communication.
expresses websocket path: "/ws/updates"| Config | Required | Description |
|---|---|---|
path | Yes | URL path for the WebSocket endpoint |
page
Serve a web page to end users (Kanvas surface). This is how machines become user-facing applications.
expresses page path: "/" title: "Order Dashboard"| Config | Required | Description |
|---|---|---|
path | Yes | URL path for the page |
title | No | Page title shown in browser tab |
a2a
Expose the machine via the Agent-to-Agent protocol, allowing other agents (local or remote) to discover and interact with it.
expresses a2a methods: ["tasks/send", "tasks/get"]| Config | Required | Description |
|---|---|---|
methods | No | Supported A2A methods (default: all standard methods) |
Authentication types
| Auth | Header | Notes |
|---|---|---|
"bearer_token" | Authorization: Bearer <token> | Token verified via Phoenix.Token |
"api_key" | X-Api-Key: <key> or ?api_key=<key> | Key verified via Phoenix.Token |
"hmac_sha256" | Signature header (provider-specific) | For webhook signature verification |
"none" | (no header) | No authentication required (default) |
Examples
API with authentication
machine ticket_classifier accepts message as text, is required
responds with team as text confidence as decimal
implements ask classify, using: "anthropic:claude-haiku-4" with task "Classify this support ticket: ${input.message}" returns team as text confidence as decimal
expresses api path: "/api/tickets/classify" method: "POST" authentication: "bearer_token"Multiple surfaces
machine live_dashboard expresses api path: "/api/metrics" method: "GET" authentication: "api_key" websocket path: "/ws/metrics" page path: "/dashboard" title: "System Metrics"Webhook receiver
machine stripe_handler accepts event_type as text, is required data as map, is required
implements decide route_event when input.event_type is "payment_intent.succeeded" compute handle_payment {status: "processed", amount: input.data.amount} when input.event_type is "charge.failed" compute handle_failure {status: "failed", reason: input.data.failure_message} otherwise compute skip {status: "ignored"}
expresses webhook path: "/hooks/stripe" authentication: "hmac_sha256" provider: "stripe"Governance
Every surface declared in expresses inherits the machine’s full governance configuration from ensures. There is no way to create a surface that bypasses governance. Specifically:
- API requests go through the same permission checks as any other invocation
- Webhook payloads are validated against the
acceptscontract before steps execute - WebSocket messages are governed per-message
- Every surface access is recorded in the behavioral ledger as a SurfaceAccess event (Inv 3: Governance Mediation)
This means a machine’s expresses section only controls how the machine is reachable, not what it is allowed to do. The ensures section controls permissions regardless of the surface.
Translations
| Language | Keyword |
|---|---|
| English | expresses |
| Spanish | expresa |
| French | expose |
| German | stellt dar |
| Japanese | 表現 |
| Chinese | 表达 |
| Korean | 표현 |
See also
- implements - Machine behavior (subscribes/publishes for event routing)
- ensures - Governance rules inherited by all surfaces
- accepts - Input contract validated on every surface request