Connect any agent to SAP Joule with A2A (Bring Your Own Agent)
Boidra Expert4 min read
SAP Joule can now talk to agents you build yourself, in any framework and on any stack, through the open Agent2Agent (A2A) protocol. SAP calls this Bring Your Own Agent (BYOA), or code-based agents. This post shows how it works using a real example: our Interface Monitoring agent for SAP AIF, wired into Joule over A2A. The full working reference lives on our boidra-labs/joule-a2a-example repo.
This is the pattern from our SAP Joule guide: build custom now, interoperate over A2A, grow into the SAP standard, with no rebuild and no lock-in.
The idea in one paragraph
Instead of building your agent inside SAP, you build it wherever you like and expose it as an A2A server. Joule discovers your agent through its Agent Card, reaches it through a named SAP BTP Destination so credentials never live in the config, and exchanges tasks over A2A. Your agent becomes just another skill the user can invoke from Joule, grounded in SAP context.
The example: an Interface Monitoring agent
The agent in this walkthrough watches SAP AIF (Application Interface Framework). A user asks Joule something like "which interfaces failed overnight and why?", and the agent queries AIF, diagnoses the failures, and answers in the Joule chat. Everything below is drawn from the real files in the repo.
Step 1: the A2A server (no code needed here)
Your agent is a normal HTTP service that speaks A2A. Two things matter for Joule:
- It publishes an Agent Card at
/.well-known/agent.json, describing the agent (name, URL, capabilities) and its skills. Joule reads the card to discover the agent and its communication URL. - It accepts A2A messages via the
message/sendmethod (A2A protocol v0.3.0, JSON-RPC over HTTPS) and returns a completed task with the answer.
You can build this in TypeScript or Python with the A2A SDK, and the framework inside (LangGraph, a plain LLM call, deterministic code) is entirely your choice. The runnable server is in the repo, so we will not repeat it here.
Step 2: the BTP Destination
This is the step people forget, and it is how Joule reaches your agent securely. Create a Destination in your BTP subaccount (Connectivity, then Destinations) pointing at the agent's URL. The credentials are resolved here at request time, never in the Joule configuration:
Name=AIF_ANALYSIS_AGENT
Type=HTTP
URL=https://aif-agent.cfapps.eu10.hana.ondemand.com
ProxyType=Internet
Authentication=OAuth2ClientCredentials
tokenServiceURL=https://your-auth/oauth/token
clientId=<client-id>
clientSecret=<client-secret>The destination name (AIF_ANALYSIS_AGENT here) is what you reference from Joule in the next step. The secret lives in the Destination, not in Joule.
Step 3: add the destination to Joule and call it from a dialog function
Joule extensibility is authored as YAML Design Time Artifacts (DTA), deployed to a Joule instance. The hierarchy is: Digital Assistant (da.sapdas.yaml), then Capability (capability.sapdas.yaml), then Scenario, Function, Action Group, Action.
To call your agent, you add a dialog function with an action of type agent-request that points at the destination. Here is the actual function from the repo that calls the AIF agent (illustrative of the shape; see the repo for the current version):
parameters:
- name: agent_context_id
optional: true
- name: agent_task_id
optional: true
- name: agent_state
optional: true
action_groups:
- actions:
- type: status-update
message: "Querying AIF monitoring..."
- actions:
- type: agent-request
agent_type: remote
system_alias: AIF_ANALYSIS_AGENT # the BTP destination name
body:
contextId: <? agent_context_id ?> # carry context across turns
result_variable: _agent_response
# The agent returns { message, intent, data } as the A2A data part of the
# completed artifact. Pull out the context id, task id, state and markdown.
- actions:
- type: set-variables
scripting_type: spel
variables:
- name: _context_id
value: "<? _agent_response.body.contextId ?>"
- name: _task_id
value: "<? _agent_response.body.id ?>"
- name: _state
value: "<? _agent_response.body.status.state ?>"
- name: _markdown
value: "<? _agent_response.body.artifacts[0].parts[1].data.message ?>"
- actions:
- type: message
message:
type: text
content: "<? _markdown ?>"
markdown: true
result:
agent_context_id: <? _context_id ?>
agent_task_id: <? _task_id ?>
agent_state: <? _state ?>A few things worth calling out:
system_aliasis the BTP destination name from step 2. That is the whole link between Joule and your agent.status-updatestreams a "working on it" message to the user while the agent runs. Joule expects the agent's response within a 60 second window (with async callbacks for longer tasks).- The agent's answer arrives as an A2A artifact. The function reads the markdown from the
datapart and renders it in the chat withmarkdown: true.
Context id: how multi-turn conversations work
Without context, your agent forgets everything after each turn, so follow-up questions do not work. A2A solves this with a context id.
Look at how it flows in the YAML above:
- Joule passes the current
agent_context_idinto the requestbodyascontextId. - The agent replies with a context id (a new one on the first turn, the same one on later turns).
- The function stores it back out in
result.agent_context_id, so Joule holds it for the next turn.
On the first message the context id is empty and the agent creates one. On every follow-up, Joule sends it back, and the agent uses it to recall the conversation, for example "and which of those are payment interfaces?" still knows which interfaces "those" refers to. The task_id and state work the same way, tracking a specific long-running task and whether it is completed, working, or needs input.
Why this pattern is the right bet
- Build now, no lock-in. Ship the agent your business needs today, in your stack, and connect it to Joule when it fits.
- Open protocol. A2A is a vendor-neutral standard (a2a-protocol.org), so the same agent can also talk to other A2A hosts.
- Governed and observable. Credentials stay in the Destination, and every step the agent takes can be instrumented, which is exactly what Boidra Platform does inside S/4HANA.
Try it
The full working reference, the A2A server, the Agent Card, the BTP destination, and the Joule dialog function above, is on our labs repo:
Go to github.com/boidra-labs/joule-a2a-example
If you want a custom Joule agent built and wired into your landscape, that is what we do. Tell us the use case.
Keep reading
New SAP AI Core Calculator 2.0
What does an SAP AI agent really cost? Explore SAP AI Core Cost Calculator 2.0, understand Capacity Units, and compare model consumption. A worked example shows how orchestration and prompt optimization can outweigh foundation-model costs and why budgeting for the complete workflow matters.
Telemetry in AI: why it matters and how it improves testing
You cannot test what you cannot see. AI telemetry - a span for every agent step - turns non-deterministic systems into something you can measure, regression-test and trust.
Joule Studio Classic vs the new Joule Studio: what changed
Joule Studio went from low-code skill building (2024) to autonomous agent building (2025) to intent-based development in Joule Studio 2.0 (2026). Here is the progression and what it means for you.