SAP Joule started as a conversational AI assistant is now a capable orchestrator that can delegate work to specialised agents — including agents you build yourself with your own code, frameworks, and models. Getting those two worlds to talk to each other is the subject of this post.
Felix Bartler's blog post, Joule A2A: Connect Code Based Agents into Joule, is the essential starting point for my exploring this space. It demonstrates clearly and practically that you can wire a custom code-based agent into Joule using the open Agent-to-Agent (A2A) protocol and a pro-code Joule capability built with Joule Studio Code Editor. If you haven't read it, do that first.
That approach works well for fast agents. But it surfaces three constraints as following.
1. The 60-second clock
Joule's synchronous communication with a code-based agent (Bring Your Own Agent, or BYOA) carries a hard 60-second timeout. Reference to official Joule Studio documentation: "With synchronous communication, Joule expects the response from agent server in < 60 seconds.". Deep research, complex reasoning chains, or agents that call slow external APIs routinely exceed this budget. When they do, the user gets an error and no response in Joule.
2. Agent Builder has no native A2A support as of March 2026
Direct A2A integration is only available to agents built with Joule Studio Code Editor (pro-code). Agents built with Joule Studio Agent Builder (low-code / no-code) have no equivalent A2A binding as of early 2026. Worth to mention that A2A support is planned in the roadmap of Joule Studio Agent Builder. Those who want to keep their Joule agent in Agent Builder, and connect it to an external code-based agent, will need a different path.
3. Lack of Real-time status update from remote agent for end user
End user doesn't receive any status update or message in realtime in Joule from remote agent about what it is going on, but have to wait until it finishes.
These constraints open up three viable integration patterns, each with its own trade-offs:
| Option 1: Direct A2A integration | Option 2: Async RESTful API | Option 3: A2A Intermediate Client Proxy | |
| Integration path | Joule (Code Editor) → A2A → Code-based Agent | Joule (Agent Builder) → SAP Build Process Automation → Async REST → Code-based Agent | Joule (Agent Builder or Code Editor) → A2A Client Service → A2A → Code-based Agent |
| Protocol | A2A (synchronous) | REST (async, poll-based) | A2A via SSE Streaming |
| Joule Tooling | Joule Studio Code Editor | Joule Studio Agent Builder | Both |
| Timeout | ~60 seconds | ~5 minutes | No hard blocking limit |
| Streaming | ❌ | ❌ | ✅ Real-time |
| Complexity | Low | Medium | Medium |
| Best for | Fast agents (< 60 s) | Long-running, (1~5 mins) | Best UX; broadest Joule compatibility |
Note: These three options merely summarise my exploration of Joule Integration with Code-based Agent. There are many other alternatives of A2A for agent in a broader context , such as ACP (Agent Communication Platform), ANP (Agent Network Protocol) etc. which are not in the scope of this blog post.
The rest of this post walks through each option in detail, using a concrete example throughout: the Deep Research Agent.
The Deep Research Agent is an autonomous multi-agent system that, given a research topic, independently plans a research strategy, spins up multiple parallel sub-agents for simultaneous research including web search, reflecting on the gathered evidence, and synthesising a comprehensive research report with inline citations.
This sample code-based agent in this blog post is a fork of the deep research agent example of langchain's deepagents SDK. It has been adapted to SAP Generative AI Hub via the SAP Cloud SDK for AI, and wrapped as a A2A server using A2A SDK and an Async RESTful API Server for integration with Joule. The agent is deployed on SAP BTP Cloud Foundry in run-time.
At a high level, the system is a two-tier agent hierarchy:
| AI Agent | Description | Role | Used by |
| Deep Research Agent | Planning research, Orchestrating the sub research agent for multi-step research, Synthesizing the research report | Root Agent | Entry Point |
| Research Agent | Research and collect information for a give research topic with web search | Sub Agent | Deep Research Agent |
Agentic Diagram of Deep Research Agent
Why is this a good test case? Because it is genuinely hard. A single research request may spawn several parallel sub-agents, each running multiple search-and-reflect cycles, before the orchestrator writes the final report. That easily exceeds 60 seconds — so it stresses all three integration patterns in a realistic way.
Next, we'll dive into the details of each option, including high-level architecture, process flow, when-to-use and its source code if you would like to do some hands-on exercise.
Sample code: deep_research_a2a
This is the most direct approach — and the closest to Felix's original blog post. A custom Joule capability (built with Joule Studio Code Editor) talks to the Deep Research Agent using the A2A protocol over HTTP. Joule acts as the A2A client; the Deep Research Agent acts as the A2A server.
joule a2a integration with deep research
┌─────────────────────────────────────┐
│ Joule as A2A Client │ deep_research_agent_capability
│ Custom Pro-Code Joule Capability │
└───────────────┬─────────────────────┘
│ HTTP (A2A protocol)
▼
┌─────────────────────────────────────┐
│ A2A Starlette Server │ app.py
│ AgentCard + AgentSkill metadata │
└───────────────┬─────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ DeepResearchAgentExecutor │ agent_executor.py
│ Manages A2A task lifecycle │
│ (working → artifact → complete) │
└───────────────┬──────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ DeepResearchAgent │ agent.py
│ create_deep_agent via deepagents SDK│
│ and SAP Cloud SDK for AI │
│ │
│ Orchestrator ──delegates─► Sub-agent│
│ (plan, synthesise, report) (search) │
└──────────────────────────────────────┘The Deep Research Agent exposes a standard A2A AgentCard at /.well-known/agent.json, describing its capabilities and the deep_research skill. The Joule capability references this agent through a named SAP BTP Destination (deep-research-agent-a2a), keeping credentials out of code.
When a user asks a research question in Joule:
research_report) back to Joule.The 60-second window is tight for deep research. The key lever is tuning two environment variables in the deployment manifest:
MAX_RESEARCHER_ITERATIONS: 2 # Limits research depth per sub-agent
MAX_CONCURRENT_RESEARCH_UNITS: 3 # Controls parallelismReducing these parameters speeds up completion at the cost of research quality. For topics where a 60-second budget is achievable, this option is the simplest to set up — one remote a2a-compatible and code-based agent deployed on Cloud Foundry, one BTP destination, and a Joule capability using an Agent Request Action.
Sample code: deep_research_api
When your agent consistently runs longer than 60 seconds, you need a fundamentally different communication model. This option replaces synchronous A2A with an asynchronous REST API and uses SAP Build Process Automation to manage the polling loop. We use this option in our sample.
One important caveat: The asynchronous callback model (bi-directional push notification) is supported Joule Agent Code Editor as Asynchronous API Request. So, a joule capability with Joule Studio Code Editor can wait up to 5 minute from remote agent until the result is returned. However, currently it requires the remote agent to have the same SAP Cloud Identity Service for authentication as Joule. Non-IAS authentication with remote agent must rely on the polling approach described above.
Joule (Agent Builder Skill)
│
▼
SAP Build Process Automation (Process)
POST /research/jobs ← submit job, receive job_id
GET /research/jobs/{id} ← poll every ~60 s until completed
│
▼
Deep Research FastAPI Service (app.py)
│
▼
DeepResearchAgent (LangGraph + deepagents + SAP Gen AI Hub)Here, the Deep Research Agent is exposed as a RESTful service with three endpoints:
| Method | Path | Description |
POST | /research | Synchronous — runs research and returns the completed report. Blocks until done (typically several minutes). |
POST | /research/jobs | Asynchronous — submits a research job, returns a job_id immediately (HTTP 202). |
GET | /research/jobs/{job_id} | Poll job status (running / completed / failed) and retrieve the result. |
POST /research/jobs), receiving a job_id immediately.GET /research/jobs/{id}) approximately every minute until status == "completed".result field (the Markdown report) and returns it to Joule as the skill output.Everything is orchestrated through a BTP Destination (deep-research-api) and an Action Project in SAP Build Process Automation. No custom code is required in the Joule layer.
In SAP Build Process Automation, three components work together:
deep-research-api destination.status != "running".Pre-built artifacts are included in the sample to accelerate setup:
Long-Run Agent Process.mtarDeep Research Skill.mtarImport them directly from SAP Build Process Automation Lobby.
The practical ceiling is around 5 minutes — the outer boundary of SAP Build Process Automation's execution context. That's enough for complex multi-step deep research under reasonable configuration.
The user experience is "fire and wait" — Joule shows a thinking indicator while the process polls in the background. There is no incremental output, which can feel opaque for lengthy research tasks.
Sample code: a2a_client_service
Demo Recording: https://youtu.be/Gx7GM8ZxnM4
This stateless RESTful A2A Client Service(Proxy) enables non-A2A-compatible AI Agents connects to any remote A2A(Agent-to-Agent) AI Agent on the fly through RESTful APIs. It routes messages between non-A2A and A2A AI Agents as illustrated below.
Non-A2A AI Agent <=REST=> A2A Client Service (Proxy) <=A2A=> A2A-compliant AI Agent
Particularly, it allows Joule Agents(regardless built with Joule Studio Agent Builder or Joule Studio Code Editor) to integrate and communicate with remote A2A AI agents in streaming mode with enterprise-grade security. The target A2A Agent server URL and authentication credentials are resolved at request time from a named SAP BTP Destination Service entry, so no credentials are ever hard-coded or revealed in the service.
Before this option existed, three gaps made seamless integration frustrating:
The A2A Client Service bridges all three gaps in a single deployed service.
Architecture of a2a-client-service
Joule Agent (Agent Builder or Code Editor)
Streamed Message Action or REST Action
│
▼
a2a-client-service (Fastify / TypeScript on CF)
POST /a2a/send — synchronous proxy
POST /a2a/stream — raw A2A SSE stream
POST /a2a/stream_normalised — normalised SSE stream
│
▼
SAP BTP Destination Service
(resolves auth: Basic / OAuth2ClientCredentials / OAuth2JWTBearer)
│
▼
Remote A2A Agent (e.g. Deep Research Agent)
/a2a/stream_normalisedThe key endpoint for Joule integration is POST /a2a/stream_normalised. It:
task, status-update, artifact-update).{
"message": {
"contextId": "...",
"taskId": "...",
"kind": "status-update",
"name": "status-update",
"parts": [{ "kind": "text", "text": "Researching AI agent frameworks...\n" }]
}
}Joule's Streamed Message Action extracts the text chunk using a JSONPath expression ($.message.parts[0].text) and renders it incrementally in the chat window — giving users a live view of the agent's progress as it happens.
With streaming, the interaction feels natural and alive: Status updates arrive as each sub-agent reports progress. The final artifact (the research report) streams in as it is generated — no waiting for a silent spinner to resolve.
For Joule Studio Code Editor, the capability uses a streamed-message action pointing at the a2a-client-service:
- type: streamed-message
method: POST
system_alias: A2A_CLIENT_SERVICE
path: /a2a/stream_normalised
headers:
Accept: "text/event-stream"
Connection: "keep-alive"
Cache-Control: "no-cache"
Content-Type: "application/json"
body: >
{
"message": "<? question ?>",
"destinationName": "deep-research-agent-a2a"
}
response_chunk_ref: $.message.parts[0].textFor Joule Studio Agent Builder, the synchronous /a2a/send endpoint of the same a2a-client-service is exposed through an Action Project — giving Agent Builder users access to any A2A agent through a familiar REST action.
Credentials never leave the BTP boundary. The destinationName in the request body is looked up at runtime by the SAP BTP Destination Service. The A2A Client Service inherit the supports all common auth types in destination:
| Destination Auth Type | Mechanism |
BasicAuthentication | Base64 user:password header |
OAuth2ClientCredentials | Client-credentials token fetch |
OAuth2JWTBearer | JWT exchange for outbound token |
... | ... |
When using /a2a/send synchronously through Agent Builder, the standard Action quota limits still apply (connection timeout: 1 min, socket timeout: 3 min). For truly long-running agents in Agent Builder, combine this with Option#2's async approach. For the best user experience with any duration, use the streaming endpoint via Code Editor — the SSE connection stays open without hitting the synchronous timeout.
destinationName — no capability changes needed
The three options are not competing alternatives — they are solutions for different situations. Here is a simple decision guide:
Start with Option 1 if your code-based agent is fast (consistently under 60 seconds), you are already using Joule Studio Code Editor, and you want the most direct, least-infrastructure path. Felix's blog post and the deep_research_a2a sample are your starting point. Tune your code-based aent to keep execution within the time budget.
Choose Option 2 if your agent is genuinely long-running (up to 5 minutes) and you prefer a low-code Joule experience with Joule Agent Builder and SAP Build Process Automation handling the polling orchestration. The pre-built .mtar artifacts make setup fast. Just be mindful of the IAS requirement if you need the full async callback model.
Choose Option 3 if user experience matters most. Streaming gives users live feedback instead of a blank wait — which is significant when research takes several minutes. It also provides the greatest flexibility: one deployed proxy service connects Joule (regardless of Agent Builder or Code Editor) to any A2A-compliant agent, with credentials managed securely through BTP destinations. Swapping to a different agent is as simple as changing a destination name.
What these three patterns represent, taken together, is the maturation of Joule as an orchestration layer. Joule does not need to be the capable agent for every task — it needs to connect the user to the right capable agent at the right time. A2A, async REST, and streaming proxies are three different connectors for doing exactly that.
The Deep Research Agent example used throughout this post is intentional. It is a realistic, non-trivial workload — it takes time, involves multiple agents working in parallel, and produces a rich artifact. Watching the same underlying agent behave differently under each integration pattern makes the trade-offs tangible rather than theoretical.
The full sample code, including all three integration patterns and the Deep Research Agent, is available in the btp-agentic-ai-use-cases GitHub repository. Each sub-folder is independently deployable with step-by-step instructions. Try the one that fits your context — and build from there.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 36 | |
| 28 | |
| 27 | |
| 26 | |
| 26 | |
| 26 | |
| 25 | |
| 24 | |
| 23 | |
| 23 |