Project Workflow Integration
The Project Workflow Integration API provides an asynchronous flow that uploads a file from an external system, automatically runs the workflow you configured on the project, lets you poll for the processing status, and returns the result as a ZIP.
When a file is uploaded, it is processed automatically while the project's active workflow is running. The caller does not need to send a separate "start execution" request.
All API requests require API key authentication.
Prerequisites
- An API key must be issued. See the Authentication page.
- Identify the numeric project ID that will receive the file. Navigate into the project page and use the value from the URL segment
projects/{number}. - The project's active workflow must be running. If it is stopped, the upload will succeed but analysis will not start, so start it from the project's Workflows menu in advance.
- The API key user must hold administrator permission on that project. Without it, the upload returns
403.
Authentication
All API requests must include an API key in the HTTP header. The API key follows Bearer token authentication.
| Header | Value |
|---|---|
Authorization |
Bearer {API_KEY} |
Replace {API_KEY} with your actual issued API key in the request.
End-to-end Flow
This API works as a three-step asynchronous flow.
- Upload the file — Call
POST /projects/{projectId}/documents/uploadand receive adocumentUuid. - Poll the status — Call
GET /external/documents/{uuid}/statusperiodically to check progress. Stop polling once it reaches a terminal status (succeededorfailed). - Download the result — Once status is
succeeded, callGET /external/documents/{uuid}/exportto download the result ZIP.
Endpoints
`POST /projects/{projectId}/documents/upload`
Uploads a file to the project. If the project's active workflow is running, the backend automatically triggers that workflow. Use the documentUuid returned in the response for the subsequent status polling and export steps.
Request Examples
cURL
curl -X POST 'https://synapfoundry.ai/api/v1/projects/{PROJECT_ID}/documents/upload' \
-H 'Authorization: Bearer {API_KEY}' \
-F 'file=@{FILE_PATH}'Python
import requests
api_key = "{API_KEY}"
file_path = "/path/to/your/file.ext"
url = "https://synapfoundry.ai/api/v1/projects/{PROJECT_ID}/documents/upload"
headers = { "Authorization": f"Bearer {api_key}" }
with open(file_path, "rb") as file:
files = { "file": file }
response = requests.post(url, headers=headers, files=files)
document_uuid = response.json()["documentUuid"]Node.js
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
const apiKey = "{API_KEY}";
const projectId = "{PROJECT_ID}";
const filePath = "/path/to/your/file.ext";
const url = `https://synapfoundry.ai/api/v1/projects/${projectId}/documents/upload`;
const form = new FormData();
form.append('file', fs.createReadStream(filePath));
const headers = {
...form.getHeaders(),
'Authorization': `Bearer ${apiKey}`,
};
axios.post(url, form, { headers }).then(response => {
const documentUuid = response.data.documentUuid;
console.log(documentUuid);
}).catch(error => {
console.error('Error:', error.response?.data || error.message);
});Headers
| Parameter | Type | Description |
|---|---|---|
| Authorization | string |
Bearer token for API request authentication. Must be provided in the format Bearer {API_KEY}. |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| projectId | number |
Yes | The numeric ID of the project to upload to. Use the value from the project page URL segment projects/{number}. |
Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| file | file |
Yes | The single file to upload. Sent via the file field of multipart/form-data. |
Success Responses
| Status Code | Media Type | Description |
|---|---|---|
| 201 Created | application/json |
Returns the identifiers of the created document on successful upload. |
Response Example
{
"documentId": 1289,
"documentUuid": "550e8400-e29b-41d4-a716-446655440000"
}Only documentUuid is used in subsequent steps. (documentId is an internal identifier.)
Error Responses
| Status Code | Message | Description |
|---|---|---|
401 Unauthorized |
"API Key authentication failed. Please use a valid API Key for your request." |
Returned when the API key is missing or invalid. |
403 Forbidden |
- | Returned when the API key user does not hold administrator permission on the project. |
413 Payload Too Large |
File size exceeds 100MB. |
Returned when the size of a single uploaded file exceeds the maximum size (100MB) allowed by the server. |
415 Unsupported Media Type |
Unsupported file format. |
Returned when the format of the uploaded file is not supported by the server. Check Supported Formats. |
`GET /external/documents/{uuid}/status`
Returns the workflow processing status of the uploaded document as a simple flag. Stop polling immediately once the status reaches a terminal value.
Status Values
| Value | Meaning | terminal? |
|---|---|---|
pending |
Waiting to be processed | no |
processing |
In progress | no |
review_pending |
Awaiting human review (HITL) | no |
succeeded |
Completed — result is ready to download | yes |
failed |
Failed / aborted / out of credits | yes |
Clients should treat any unknown value as terminal and stop polling. The backend safely normalizes unknown internal states to
failed, but defensive handling is recommended.
Recommended Polling Policy
- Interval: Start at 5 seconds with exponential backoff (e.g., 5s → 10s → 20s → up to 60s).
- Maximum duration: Varies by document size and workflow, but typically within 30 minutes. Contact operations if it exceeds.
- Retries: Retry
5xxresponses with backoff. Do not retry4xxresponses.
Request Examples
cURL
curl -X GET 'https://synapfoundry.ai/api/v1/external/documents/{DOC_UUID}/status' \
-H 'Authorization: Bearer {API_KEY}'Python
import requests
import time
api_key = "{API_KEY}"
url = "https://synapfoundry.ai/api/v1/external/documents/{DOC_UUID}/status"
headers = { "Authorization": f"Bearer {api_key}" }
interval = 5
max_seconds = 1800 # 30-minute safety cap
elapsed = 0
while elapsed < max_seconds:
response = requests.get(url, headers=headers)
if response.status_code >= 500:
# 5xx: retry after backoff
time.sleep(interval)
elapsed += interval
interval = min(interval * 2, 60)
continue
response.raise_for_status() # 4xx: raise immediately and stop polling
status = response.json()["status"]
if status in ("succeeded", "failed"):
break
time.sleep(interval)
elapsed += interval
interval = min(interval * 2, 60)
else:
raise TimeoutError("Polling exceeded 30 minutes.")Node.js
const axios = require('axios');
const apiKey = "{API_KEY}";
const docUuid = "{DOC_UUID}";
const url = `https://synapfoundry.ai/api/v1/external/documents/${docUuid}/status`;
async function pollStatus() {
let interval = 5000;
const maxMs = 30 * 60 * 1000; // 30-minute safety cap
let elapsed = 0;
while (elapsed < maxMs) {
try {
const { data } = await axios.get(url, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
if (data.status === 'succeeded' || data.status === 'failed') {
return data.status;
}
} catch (err) {
const code = err.response?.status;
if (code && code >= 400 && code < 500) {
// 4xx: do not retry — stop polling
throw err;
}
// 5xx / network errors: retry after backoff
}
await new Promise(r => setTimeout(r, interval));
elapsed += interval;
interval = Math.min(interval * 2, 60000);
}
throw new Error('Polling exceeded 30 minutes.');
}Headers
| Parameter | Type | Description |
|---|---|---|
| Authorization | string |
Bearer token for API request authentication. Must be provided in the format Bearer {API_KEY}. |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| uuid | string |
Yes | The documentUuid returned from the upload response. |
Success Responses
| Status Code | Media Type | Description |
|---|---|---|
| 200 OK | application/json |
Returns the current processing status. |
Response Example
{ "status": "processing" }Error Responses
| Status Code | Message | Description |
|---|---|---|
401 Unauthorized |
"API Key authentication failed. Please use a valid API Key for your request." |
Returned when the API key is missing or invalid. |
404 Not Found |
{ "statusCode": 404, "message": "Not Found" } |
Returned identically when the UUID does not exist, does not belong to the caller, or is malformed (to prevent information disclosure). Stop polling. |
`GET /external/documents/{uuid}/export`
Downloads the result of the workflow's final component as a ZIP for a document that completed successfully. Call this right after the polling API returns succeeded.
ZIP Contents by Final Component
The contents of the ZIP differ depending on the workflow's final output component.
| Final Component | ZIP Contents |
|---|---|
| Analyze | The cached analysis result ZIP as-is |
| Review | The review-completed result ZIP |
| Transform Analysis Result | Conversion result files bundled into a single ZIP |
| LLM processing | A ZIP containing the LLM response text as a single .md or .txt entry |
Because the internal layout differs per component, integration clients are recommended to either know the final output component type in advance or process the ZIP entries generically (by iterating entries).
Request Examples
cURL
curl -L 'https://synapfoundry.ai/api/v1/external/documents/{DOC_UUID}/export' \
-H 'Authorization: Bearer {API_KEY}' \
-o result.zipPython
import requests
api_key = "{API_KEY}"
url = "https://synapfoundry.ai/api/v1/external/documents/{DOC_UUID}/export"
response = requests.get(url, headers={ "Authorization": f"Bearer {api_key}" })
with open("result.zip", "wb") as f:
f.write(response.content)Node.js
const axios = require('axios');
const fs = require('fs');
const apiKey = "{API_KEY}";
const docUuid = "{DOC_UUID}";
const url = `https://synapfoundry.ai/api/v1/external/documents/${docUuid}/export`;
axios.get(url, {
headers: { 'Authorization': `Bearer ${apiKey}` },
responseType: 'stream'
}).then(response => {
response.data.pipe(fs.createWriteStream('result.zip'));
}).catch(error => {
console.error('Error:', error.response?.data || error.message);
});Headers
| Parameter | Type | Description |
|---|---|---|
| Authorization | string |
Bearer token for API request authentication. Must be provided in the format Bearer {API_KEY}. |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| uuid | string |
Yes | The documentUuid returned from the upload response. |
Success Responses
| Status Code | Media Type | Description |
|---|---|---|
| 200 OK | application/zip |
Returns the workflow result ZIP. The response also includes Content-Disposition: attachment; filename="<uuid>.zip". |
Error Responses
| Status Code | Message | Description |
|---|---|---|
401 Unauthorized |
"API Key authentication failed. Please use a valid API Key for your request." |
Returned when the API key is missing or invalid. |
404 Not Found |
{ "statusCode": 404, "message": "Not Found" } |
Returned identically when the UUID does not exist, does not belong to the caller, or is malformed. |
409 Conflict |
{ "code": "NOT_EXPORTABLE", "status": "<current status>" } |
Document is not yet exportable. Go back to the polling API, wait for succeeded, and retry. |
500 Internal Server Error |
Failed to process the request due to an internal server error. |
A transient internal error. Retry with exponential backoff. |