Integrate Astron Agent into Your Application
Astron Agent can run workflows for another application through its published workflow API. Your application stays responsible for its own UI and business logic, while Astron Agent provides the workflow, model, knowledge, tool, and RPA orchestration behind an HTTP/SSE boundary.
This is a service integration, not an in-process library integration. The supported consumer boundary is the public workflow API exposed by Astron Agent's gateway. Internal Python and Java modules are implementation details and are not published as stable SDK packages.
When to use this integration
Use the workflow API when you want to:
- add an AI workflow to an existing web, mobile, backend, or automation project;
- keep model and tool orchestration in Astron Agent instead of rebuilding it in every application;
- stream intermediate output to a user interface;
- reuse one published workflow from multiple services while keeping application credentials separate.
If you only want to evaluate or operate the full platform, start with Quick Start or Deployment instead.
Integration boundary
Your application
│ POST /workflow/v1/chat/completions
│ Authorization: Bearer <application-key>:<application-secret>
▼
Astron Agent gateway (Nginx)
│ validates the application and forwards its App ID
▼
Published workflow
└─ models · knowledge · tools/MCP · RPACall the gateway address shown after publishing. Do not call core-workflow:7880, /internal/gateway/auth/**, or other container-only endpoints directly: those routes are internal, and direct calls bypass the supported gateway boundary.
1. Publish a workflow as an API
- Create and debug a workflow in the Astron Agent console.
- Select Publish in the workflow editor.
- Choose Publish as API and open its configuration.
- Create or select an application, then complete the publication flow.
- Copy the generated values:
- Service URL
- Flow ID
- API Key
- API Secret
Keep the API Secret in a server-side secret store. Do not put it in browser code, mobile packages, source control, screenshots, or client-visible logs.
2. Call the published workflow
The public endpoint is:
POST <ASTRON_BASE_URL>/workflow/v1/chat/completionsUse the exact Service URL displayed by the console when it differs from the path above. Authentication uses the application credentials in one header:
Authorization: Bearer <application-key>:<application-secret>
Content-Type: application/jsonA minimal request is:
{
"flow_id": "<FLOW_ID>",
"uid": "user-123",
"stream": true,
"parameters": {
"query": "Summarize this support request"
}
}flow_id and parameters are required. The available keys inside parameters come from the workflow's Start node, so replace query with the inputs defined by your published workflow.
Optional request fields:
| Field | Type | Purpose |
|---|---|---|
uid | string | Your end-user identifier (up to 40 characters). |
stream | boolean | true for SSE streaming; false for one JSON response. Defaults to true. |
chat_id | string | Your conversation identifier (up to 128 characters). Reuse it when continuing a conversation. |
history | array | Earlier messages as `{ "role": "user" |
ext | object | Integration-specific metadata forwarded with the request. |
version | string | Published workflow version when your environment requires one. |
cURL
export ASTRON_BASE_URL="https://astron.example.com"
export ASTRON_API_KEY="replace-me"
export ASTRON_API_SECRET="replace-me"
export ASTRON_FLOW_ID="replace-me"
curl --no-buffer \
--request POST "$ASTRON_BASE_URL/workflow/v1/chat/completions" \
--header "Authorization: Bearer $ASTRON_API_KEY:$ASTRON_API_SECRET" \
--header "Content-Type: application/json" \
--data "{
\"flow_id\": \"$ASTRON_FLOW_ID\",
\"uid\": \"user-123\",
\"chat_id\": \"conversation-456\",
\"stream\": true,
\"parameters\": {
\"query\": \"Summarize this support request\"
}
}"Python
This example uses only the Python standard library. Run it in a backend service, not in browser-delivered code.
import json
import os
import urllib.request
base_url = os.environ["ASTRON_BASE_URL"].rstrip("/")
credential = f'{os.environ["ASTRON_API_KEY"]}:{os.environ["ASTRON_API_SECRET"]}'
payload = {
"flow_id": os.environ["ASTRON_FLOW_ID"],
"uid": "user-123",
"chat_id": "conversation-456",
"stream": True,
"parameters": {"query": "Summarize this support request"},
}
request = urllib.request.Request(
f"{base_url}/workflow/v1/chat/completions",
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": f"Bearer {credential}",
"Content-Type": "application/json",
"Accept": "text/event-stream",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=1800) as response:
for raw_line in response:
line = raw_line.decode("utf-8").strip()
if not line.startswith("data:"):
continue
event = json.loads(line.removeprefix("data:").strip())
if event.get("code") != 0:
raise RuntimeError(event.get("message", "workflow failed"))
choice = (event.get("choices") or [{}])[0]
delta = choice.get("delta") or {}
if delta.get("content"):
print(delta["content"], end="", flush=True)
finish_reason = choice.get("finish_reason")
if finish_reason == "stop":
break
if finish_reason == "interrupt":
print("\nWorkflow paused and requires a reply.")
breakNode.js 18+
const baseUrl = process.env.ASTRON_BASE_URL.replace(/\/$/, "");
const authorization = `Bearer ${process.env.ASTRON_API_KEY}:${process.env.ASTRON_API_SECRET}`;
const response = await fetch(`${baseUrl}/workflow/v1/chat/completions`, {
method: "POST",
headers: {
Authorization: authorization,
"Content-Type": "application/json",
Accept: "text/event-stream"
},
body: JSON.stringify({
flow_id: process.env.ASTRON_FLOW_ID,
uid: "user-123",
chat_id: "conversation-456",
stream: true,
parameters: { query: "Summarize this support request" }
})
});
if (!response.ok || !response.body) {
throw new Error(`Astron request failed: ${response.status} ${await response.text()}`);
}
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
let finished = false;
while (!finished) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
const frames = buffer.split("\n\n");
buffer = frames.pop() ?? "";
for (const frame of frames) {
const dataLine = frame.split("\n").find((line) => line.startsWith("data:"));
if (!dataLine) continue;
const event = JSON.parse(dataLine.slice(5).trim());
if (event.code !== 0) throw new Error(event.message || "workflow failed");
const choice = event.choices?.[0];
if (choice?.delta?.content) process.stdout.write(choice.delta.content);
if (choice?.finish_reason === "interrupt") {
console.log("\nWorkflow paused and requires a reply.");
finished = true;
}
if (choice?.finish_reason === "stop") finished = true;
}
}3. Handle the response
Streaming mode
With stream: true, the response media type is text/event-stream. Each frame contains a data: line whose value is JSON. Useful fields include:
{
"code": 0,
"message": "Success",
"id": "request-or-session-id",
"choices": [
{
"delta": {
"role": "assistant",
"content": "incremental output",
"reasoning_content": ""
},
"finish_reason": null
}
],
"workflow_step": {
"seq": 3,
"progress": 0.5
}
}Consumer rules:
- append
choices[0].delta.contentwhen it is non-empty; - treat
code != 0as a workflow error even if the HTTP connection was established successfully; - ignore heartbeat frames with
finish_reason: "ping"; - finish on
finish_reason: "stop"; - handle
finish_reason: "interrupt"only if your workflow contains an interactive pause; - do not depend on every intermediate
workflow_stepfield as a stable business contract.
Non-streaming mode
Set stream to false to receive one JSON response. The response uses the same top-level shape; inspect code, message, choices, and usage rather than assuming a plain text body.
4. Resume an interrupted workflow
If a frame has finish_reason: "interrupt", save event_data.event_id. After collecting the user's answer, call:
POST <ASTRON_BASE_URL>/workflow/v1/resumeUse the same Authorization header:
curl --no-buffer \
--request POST "$ASTRON_BASE_URL/workflow/v1/resume" \
--header "Authorization: Bearer $ASTRON_API_KEY:$ASTRON_API_SECRET" \
--header "Content-Type: application/json" \
--data '{
"event_id": "<EVENT_ID>",
"event_type": "resume",
"content": "The user reply"
}'The resume response follows the mode of the interrupted request. Event IDs are runtime state: resume promptly and handle an expired or already-resumed event as an error.
Production checklist
- Use HTTPS. The default local deployment listens on HTTP; terminate TLS at Nginx or an upstream ingress before exposing the API outside a trusted network.
- Keep credentials server-side. If a browser or mobile app needs the capability, call your own backend first and let that backend call Astron Agent.
- Separate applications and credentials. Use different Astron applications for environments or consumers that need independent rotation and revocation.
- Set streaming-aware timeouts. The bundled gateway allows long-lived workflow streams, but every proxy and client in front of it must also disable buffering and allow suitable read timeouts.
- Use stable IDs. Generate
uidandchat_idfrom your system; do not put secrets or sensitive personal data in either value. - Validate workflow inputs. Treat
parametersas untrusted input and constrain it before passing values to tools, databases, or RPA actions. - Record trace IDs safely. The response
idis useful for troubleshooting. Log it with the status and duration, but redact credentials and sensitive prompts. - Plan for workflow changes. Start-node inputs and End-node outputs are the consumer contract. Review them before publishing a new version and test clients against the published workflow.
What is not a supported dependency contract
The following can be useful when contributing to Astron Agent itself, but external applications should not depend on them as stable APIs:
- importing packages directly from
core/agent,core/workflow, orcore/common; - calling container DNS names or internal service ports;
- sending
X-Consumer-Usernameyourself; - calling
/internal/gateway/auth/**or unpublished/workflow/v1/**routes; - reading or writing Astron Agent databases directly.
For repository-level extension work, see Project Modules. For reusable workflow definitions, see Workflow Examples.
Troubleshooting
| Symptom | Check |
|---|---|
401 or a missing/malformed credential error | Header must be exactly Authorization: Bearer <application-key>:<application-secret>. Confirm neither value is empty. |
Failed to get application | Confirm the API Key and Secret belong to the application selected at publication time. |
| Workflow not found or not authorized | Use the Flow ID shown for the published API, not the App ID or an editor-only ID. |
| Parameter validation error | Send a parameters object whose keys and value types match the Start node. |
| Output arrives all at once | Disable response buffering in every reverse proxy and use an SSE-capable HTTP client. |
| Stream stays open without text | Heartbeat frames are normal while a long-running node executes; enforce your own overall deadline. |
| Works inside Docker but not externally | Call the published gateway host and exposed port, then check DNS, firewall, TLS, and proxy routing. |
See the FAQ, Configuration Reference, and Deployment Guide for additional operational guidance.
