Call Your App over HTTP
The full HTTP contract for invoking a deployed Dynamiq App — auth, request body, synchronous responses, SSE streaming, async callbacks, and error codes.
Every deployed App serves its own HTTPS endpoint. You send a POST request with your workflow's input, and get the result back synchronously (one response when the run finishes), as a Server-Sent Events (SSE) stream, or asynchronously to a callback URL you provide. This page is the complete contract; the Integration tab of your App page generates the same requests prefilled with your hostname and input schema.
Base URL
Each App has a unique hostname, shown (with a copy button) in the Hostname field of the App page header. All requests go to:
https://<your-app-hostname>The platform routes the request to your App based on the hostname itself — there is no app ID in the path. Don't share hostnames between Apps; each App gets its own, and it stays stable across redeployments.
Authentication
If the App was deployed with Endpoint Authorization enabled (the default — Authorization: Enabled in the header), every request must carry an Access Key as a Bearer token:
Authorization: Bearer $DYNAMIQ_ACCESS_KEYAccess Keys are org- or project-scoped: a project-scoped key only works for Apps in that project. Public Apps (Authorization: Disabled) accept requests without the header.
Only Access Keys work here. A Personal Access Token is scoped to the management API, not to deployed Apps, and is rejected with 403 even when the token itself is valid.
Request body
The body is JSON, sent with Content-Type: application/json (the App also accepts multipart/form-data for file inputs; any other content type returns 415). The only required field is input, whose keys are exactly the fields defined on your workflow's Input node — the Integration and Test tabs both render these fields for you.
inputobjectrequiredstreambooleanexecution_modestringcallbacksarraylast_node_outputbooleanerror_on_node_failurebooleanThe examples below use a workflow whose Input node defines a single question field — replace the input object with your own schema.
Synchronous requests
POST with "stream": false (or omit it) and the connection stays open until the workflow finishes; the response is the workflow output as JSON with status 200.
curl -X POST "https://<your-app-hostname>" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DYNAMIQ_ACCESS_KEY" \
-d '{
"input": {
"question": "What can you do?"
},
"stream": false
}'import os
import requests
import json
endpoint = "https://<your-app-hostname>"
token = os.getenv("DYNAMIQ_ACCESS_KEY") # Generate Access Key in the UI settings
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}',
}
# Payload: Modify input schema as per the input node schema defined in the UI
payload = {
"input": {
"question": "What can you do?"
},
"stream": False,
}
# Make a POST request to the deployed endpoint
response = requests.post(endpoint, json=payload,
headers=headers, stream=False)
if response.status_code == 200:
try:
data = response.json()
print("Response:", json.dumps(data, indent=4))
except json.JSONDecodeError as e:
print(f"Failed to decode JSON response: {e}")
else:
print(f"""Failed to connect to {endpoint}.
Status code: {response.status_code}. Response: {response.text}""")// HTTP Client without Streaming
const endpoint = "https://<your-app-hostname>";
const token = process.env.DYNAMIQ_ACCESS_KEY; // Access key should be securely stored
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
};
// Payload: Modify input schema as per the input node schema defined in the UI
const payload = {
"input": {
"question": "What can you do?"
},
"stream": false
};
async function fetchWithoutStreaming() {
const response = await fetch(endpoint, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`Failed to connect to ${endpoint}. Status code: ${response.status}. Response: ${await response.text()}`);
}
const data = await response.json();
console.log("Response:", JSON.stringify(data, null, 4));
}
fetchWithoutStreaming();Synchronous calls hold the HTTP connection for the full run. For workflows that take more than a few seconds, prefer streaming or async callbacks — see Streaming and async for guidance.
Streaming over SSE
POST with "stream": true and the App responds with Content-Type: text/event-stream, sending output as it is generated instead of one final response. Each SSE data: line carries a JSON message. Messages whose event field matches the streaming event name configured on your workflow's streaming-enabled node ("data" by default) carry incremental chunks of model output ("deltas") at data.choices[0].delta.content.
curl -N -X POST "https://<your-app-hostname>" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DYNAMIQ_ACCESS_KEY" \
-d '{
"input": {
"question": "What can you do?"
},
"stream": true
}'import os
import requests
import json
endpoint = "https://<your-app-hostname>"
token = os.getenv("DYNAMIQ_ACCESS_KEY") # Generate Access Key in the UI settings
streaming_event = "data" # Event name configured in the UI
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}',
}
# Payload: Modify input schema as per the input node schema defined in the UI
payload = {
"input": {
"question": "What can you do?"
},
"stream": True,
}
# Make a POST request to the deployed endpoint
response = requests.post(endpoint, json=payload,
headers=headers, stream=True)
if response.status_code == 200:
# consume server-sent events (SSE)
for line in response.iter_lines(decode_unicode=True):
if line.startswith("data:"):
data = line[len("data:"):].strip()
try:
json_data = json.loads(data)
if json_data.get("event") == streaming_event:
content = json_data.get("data", {}).get("choices", [{}])[0].get("delta", {}).get("content")
if content:
print(content, end='')
except json.JSONDecodeError as e:
print(f"Invalid JSON format: {data} - Error: {e}")
else:
print(f"""Failed to connect to {endpoint}.
Status code: {response.status_code}. Response: {response.text}""")// HTTP Client with Server-Sent Events (Streaming)
const endpoint = "https://<your-app-hostname>";
const token = process.env.DYNAMIQ_ACCESS_KEY; // Access key should be securely stored
const streamingEvent = "data"; // Event name configured in the UI
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
};
// Payload: Modify input schema as per the input node schema defined in the UI
const payload = {
"input": {
"question": "What can you do?"
},
"stream": true
};
async function fetchWithStreaming() {
const response = await fetch(endpoint, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`Failed to connect to ${endpoint}. Status code: ${response.status}. Response: ${await response.text()}`);
}
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Process complete SSE lines
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep the last incomplete line in buffer
for (const line of lines) {
if (line.startsWith('data:')) {
const data = line.substring(5).trim();
const jsonData = JSON.parse(data);
if (jsonData.event === streamingEvent) {
const content = jsonData.data?.choices?.[0]?.delta?.content;
if (content) {
console.log(content);
}
}
}
}
}
}
fetchWithStreaming();Async with callbacks
For long runs where you don't want any open connection, set "execution_mode": "async" and pass one or more callbacks (up to 5; each url must be a publicly reachable HTTPS URL). auth is optional and supports only "type": "bearer"; when set, the token is sent as Authorization: Bearer <token> on the callback request.
The request body looks like this:
{
"input": {
"question": "What can you do?"
},
"execution_mode": "async",
"callbacks": [
{
"url": "https://your-callback-url.example.com/webhook",
"auth": {
"type": "bearer",
"token": "your-callback-auth-token"
},
"metadata": {
"key": "value"
}
}
]
}The App responds immediately with status 202 and a request ID:
{
"id": "f7c7bb61-4f9f-4fd0-940b-98ebd5bd2777",
"status": "accepted"
}When the run finishes, your callback URL receives a POST request with the same id:
{
"id": "f7c7bb61-4f9f-4fd0-940b-98ebd5bd2777",
"status": "succeeded",
"timestamp": "2026-03-26T08:34:27.337615881Z",
"output": {
"output": "How are you?"
},
"metadata": {
"one": "two"
}
}status is "succeeded" or "failed" — check it before reading output, which is the workflow output on success and may be omitted on failure. The metadata you set on the callback is echoed back, and id matches the 202 response, so you can correlate the result with your original request. See Webhooks and events for callback delivery details.
Error codes
| Status | Meaning |
|---|---|
400 | Invalid request — malformed JSON, missing input, invalid field values, or callbacks sent without "execution_mode": "async". The body includes validation details per field. |
401 | Missing or invalid Access Key on a private App. |
403 | The token is valid but is not an Access Key (e.g. a Personal Access Token), or the Access Key is scoped to a different project or organization than the App. Also returned with code subscription_limit_reached when your organization's monthly App invocation quota is exhausted. |
404 | No App matches the hostname, or the App was deleted or archived. |
415 | Unsupported Content-Type — send application/json (or multipart/form-data for file inputs). |
Non-2xx responses use a consistent JSON error envelope with a message and, for validation failures, per-field details.
Beyond a single request
The same hostname also serves the Runs API (/v1/runs, /v1/files, …) for run management: create background runs, list and filter runs, re-attach to a live event stream, cancel runs, and answer human-in-the-loop requests mid-run. The Integration tab additionally generates a WebSocket variant (wss://<your-app-hostname>) for bi-directional streaming.
Deploy a Workflow App
Turn a saved workflow version into a live App from the editor or the Deployments page, then tour every tab of the App page.
The Runs API
Manage App executions on your App's hostname — upload files, create sync/streaming/background runs, list and inspect runs, cancel, send mid-run input, and replay events.