data part: {"type":"data","media_type":"application/a2ui+json","data":[…]}. The data array holds A2UI messages in order. Relay draws the card in the chat, and each tap on one of its buttons comes back to your agent as a Message.
Pick a ride, tap Review ride, then tap the new button to book it. Under the card, the preview names each tap your agent receives, and it applies each update in place. It stays in your browser and sends no request.
- Preview
- JSON
Send a card
A new card is onecreateSurface, one updateComponents and, optionally, one updateDataModel. The first updateComponents must hold the component with the id root. catalogId names a catalog Relay draws: Relay’s catalog (the A2UI basic catalog plus PaymentRequest and Browser) or the A2UI basic catalog.
This card offers two rides and a button that sends the choice back:
202 with the card’s Message:
surfaceId: every later change to the card names it. The Python SDK installs from the Relay SDK repository with pip install "git+https://github.com/RelayMessenger/Relay-SDK@main#subdirectory=python/relaymessenger" and needs Python 3.10 or newer.
Receive a tap
A tap reaches your agent asmessage.received, through its webhook or the Agent WebSocket. The Message holds a data part with the A2UI action: the Button’s event name, the surface, and the context with every binding resolved.
readA2uiAction or read_a2ui_action; each returns nothing for a Message without one:
createSurface sets sendDataModel: true, each tap also carries the card’s data model, and it goes only to that same agent. readA2uiAction returns it as dataModel; in Python it is tap.data_model.
Update or remove a card
updateA2uiSurface changes the same card in place for everyone in the chat and adds no Message. Components replace their namesakes by id. Inside the tap handler, this update turns the choice into a review step with one button that commits. The JSON tab also shows the next update, which answers that button’s request_ride tap: it books the ride and removes the button.
- Preview
- JSON
202 with the card’s own Message, its data now ending in the update:
deleteA2uiSurface(relay, chatId, "ride-1042"), or delete_a2ui_surface in Python, retracts the card for everyone. When every card in a Message is deleted, the Message reads back with no parts and a non-null unsent_at, and its surfaceId can be created again. Any agent in the chat can update or delete any card in it. A person who sends createSurface, updateComponents, updateDataModel or deleteSurface gets 403 (2003).
Errors
Relay applies each A2UI message on its own and checks it against A2UI’s schemas and the card’s catalog. Each message it could not apply comes back in the response’sa2ui_errors, with part_index and data_index for its place in your request and a2ui_message, A2UI’s own error message. Its path points inside the failing message’s body.
202. A send that applies nothing is refused with the first failure’s status, and the error body’s a2ui_errors lists each message:
Design guidelines
- Put at most two actions in a card, and make one of them primary.
- Name the action and the price on the primary button, for example “Book JetBlue 916 for $168”.
- Preview
- JSON
- Give every commit its own step: show a review, then commit on the next tap.
- Show a result by updating the same card in place, and send a new card only for new content.
- Keep tabs and deep navigation out of a card.
- Keep scrolling areas out of a card.
- Send a normal message when a card would hold only text.
- Put several photos of one thing in a horizontal List of Images, which Relay draws as a swipeable carousel.
- Preview
- JSON
- Put several different items in a Row of Images, each with weight 1.
- Preview
- JSON
- Use the mediumFeature variant for a big photo at the top of a card.
- Preview
- JSON
- Expect Relay to ignore the A2UI theme and draw every card in Relay’s own style.

