Export API
The Export API lets you query the results of a motion's execution. Use it to retrieve contacts and accounts that have been processed by a motion, along with their evaluation outcomes and action summaries.
This API is ideal for building custom integrations that need to poll for motion results at regular intervals (e.g., every minute).
Authentication
Pass your server secret key in the Authorization header:
Authorization: Bearer sk_live_...
Endpoint
GET https://app.syftdata.com/api/motion/{motionId}/export
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
cursor | string | — | Pagination cursor. Pass the nextCursor value returned by the previous response. Treat it as an opaque token. |
limit | number | 50 | Max items per page (max 100) |
since | ISO8601 string | — | Only return runs created/updated after this timestamp |
until | ISO8601 string | — | Only return runs created/updated before this timestamp |
status | string | completed | Filter by status (e.g., completed, failed, skipped, in_progress). Defaults to completed. To retrieve runs with other statuses, you must explicitly provide the status parameter. |
order | string | asc | Sort order: asc, desc. Note: Runs are always ordered by their execution timestamp. |
fields | string | — | Comma-separated list of top-level fields to include in each item (e.g., status,account,contacts). Omit to return all fields. runId is always included. Use this to shrink the response to only what you need. See Selecting fields. |
Examples
Get recent motion exports
curl -X GET "https://app.syftdata.com/api/motion/{motionId}/export?limit=50" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
Poll for new exports since last check
curl -X GET "https://app.syftdata.com/api/motion/{motionId}/export?since=2024-01-01T00:00:00Z&order=asc" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
Get failed exports
curl -X GET "https://app.syftdata.com/api/motion/{motionId}/export?status=failed" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
Return only specific fields
curl -X GET "https://app.syftdata.com/api/motion/{motionId}/export?fields=status,account,contacts" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
Paginate through results
# First page
curl -X GET "https://app.syftdata.com/api/motion/{motionId}/export?limit=50" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
# Next page (use nextCursor from previous response)
curl -X GET "https://app.syftdata.com/api/motion/{motionId}/export?limit=50&cursor=clxxx..." \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
Response
{
"items": [
{
"runId": "clxxx...",
"status": "completed",
"createdAt": "2024-01-01T10:00:00.000Z",
"updatedAt": "2024-01-01T10:05:00.000Z",
"completedAt": "2024-01-01T10:05:00.000Z",
"targetType": "MarContact",
"triggerSource": "webhook",
"account": {
"domain": "example.com",
"name": "Example Inc",
"employeeCount": 100
},
"contacts": [
{
"email": "lead@example.com",
"firstName": "John",
"lastName": "Doe",
"title": "VP Marketing"
}
],
"session": null,
"evaluationSummary": "High intent lead from pricing page",
"triggerReason": "Matched ICP criteria",
"actionPlanSummary": "Send welcome email",
"error": null
}
],
"nextCursor": "clxxx...",
"hasMore": true
}
Response Fields
| Field | Type | Description |
|---|---|---|
items | array | Array of motion run results |
nextCursor | string | null | Cursor for the next page (null if no more items) |
hasMore | boolean | Whether more items exist beyond this page |
Run Item Fields
| Field | Type | Description |
|---|---|---|
runId | string | Unique identifier for the run |
status | string | Run status: completed, failed, skipped, open, running, etc. |
createdAt | string | ISO8601 timestamp when run was created |
updatedAt | string | ISO8601 timestamp when run was last updated |
completedAt | string | null | ISO8601 timestamp when run completed (null if still in progress) |
targetType | string | Type of target: MarContact, MarCompany, MarSessionSummary |
triggerSource | string | null | Source that triggered the run: webhook, csv, mcp, etc. |
account | object | null | Company data (see Webhook Company Object) |
contacts | array | Contact data (see Webhook Contact Fields) |
session | object | null | Session data if applicable (see Webhook Session Object) |
evaluationSummary | string | null | AI evaluation summary |
triggerReason | string | null | Reason the motion was triggered |
actionPlanSummary | string | null | Summary of actions taken |
error | string | null | Error message if run failed |
Selecting fields
By default each item includes every field above. For large exports — or when you only need a subset — pass fields with a comma-separated list of the top-level fields you want. This reduces response size and processing time, which is especially useful when feeding results into an LLM or other token-sensitive pipeline.
# Only return run status, account, and contacts
curl -X GET "https://app.syftdata.com/api/motion/{motionId}/export?fields=status,account,contacts" \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY"
Each returned item then contains only the requested fields (plus runId):
{
"items": [
{
"runId": "clxxx...",
"status": "completed",
"account": { "domain": "example.com", "name": "Example Inc" },
"contacts": [{ "email": "lead@example.com", "firstName": "John" }]
}
],
"nextCursor": "clxxx...",
"hasMore": true
}
Notes:
runIdis always included, even if not listed infields.- Omitting
fields(or passing an empty value) returns all fields — fully backward compatible. - Omitting the heavy fields (
account,contacts,session) also skips the underlying data lookups, so narrowingfieldsmakes requests faster, not just smaller. - Valid field names:
runId,status,createdAt,updatedAt,completedAt,targetType,triggerSource,account,contacts,session,evaluationSummary,triggerReason,actionPlanSummary,error. An unrecognized name returns a400error.
Polling Pattern
For efficient minute-interval polling, use this pattern:
- Track the
completedAt(orupdatedAt) of the last processed item - Poll with
since={lastTimestamp}&order=asc - Process items and update your tracking timestamp
- Continue with
cursor={nextCursor}ifhasMore=true
let lastTimestamp = loadLastCheckpoint();
while (true) {
const url = `/api/motion/${motionId}/export?since=${lastTimestamp}&order=asc`;
const { items, nextCursor, hasMore } = await fetch(url);
for (const item of items) {
await processRunItem(item);
lastTimestamp = item.completedAt || item.updatedAt;
}
if (!hasMore) {
saveCheckpoint(lastTimestamp);
await sleep(60000); // Wait 1 minute
}
}
Error Responses
See Errors for the shared error responses. The Export API also returns:
400{ "error": "Invalid 'since' timestamp format. Use ISO8601." }for an invalid timestamp400{ "error": "Invalid fields: ... Valid fields: ..." }when one or morefieldsvalues are not recognized404{ "error": "Motion not found" }when the motion doesn't exist or belongs to another workspace
Limits
- Maximum 100 items per page (requests for more are capped automatically)
- Default page size is 50 items