Skip to main content

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

All requests must be authenticated using your Server Secret Key (starts with sk_live_). You can find this in your Syft settings.

Pass the key in one of the following ways:

MethodExample
Authorization headerAuthorization: Bearer sk_live_...
x-syft-secret-key headerx-syft-secret-key: sk_live_...

Endpoint

GET https://app.syftdata.com/api/motion/{motionId}/export

Query Parameters

ParameterTypeDefaultDescription
cursorstringPagination cursor. Pass the nextCursor value returned by the previous response. Treat it as an opaque token.
limitnumber50Max items per page (max 100)
sinceISO8601 stringOnly return runs created/updated after this timestamp
untilISO8601 stringOnly return runs created/updated before this timestamp
statusstringcompletedFilter 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.
orderstringascSort order: asc, desc. Note: Runs are always ordered by their execution timestamp.
fieldsstringComma-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

FieldTypeDescription
itemsarrayArray of motion run results
nextCursorstring | nullCursor for the next page (null if no more items)
hasMorebooleanWhether more items exist beyond this page

Run Item Fields

FieldTypeDescription
runIdstringUnique identifier for the run
statusstringRun status: completed, failed, skipped, open, running, etc.
createdAtstringISO8601 timestamp when run was created
updatedAtstringISO8601 timestamp when run was last updated
completedAtstring | nullISO8601 timestamp when run completed (null if still in progress)
targetTypestringType of target: MarContact, MarCompany, MarSessionSummary
triggerSourcestring | nullSource that triggered the run: webhook, csv, mcp, etc.
accountobject | nullCompany data (see Webhook Company Object)
contactsarrayContact data (see Webhook Contact Fields)
sessionobject | nullSession data if applicable (see Webhook Session Object)
evaluationSummarystring | nullAI evaluation summary
triggerReasonstring | nullReason the motion was triggered
actionPlanSummarystring | nullSummary of actions taken
errorstring | nullError 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:

  • runId is always included, even if not listed in fields.
  • 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 narrowing fields makes 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 a 400 error.

Polling Pattern

For efficient minute-interval polling, use this pattern:

  1. Track the completedAt (or updatedAt) of the last processed item
  2. Poll with since={lastTimestamp}&order=asc
  3. Process items and update your tracking timestamp
  4. Continue with cursor={nextCursor} if hasMore=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

StatusBodyDescription
400{ "error": "Invalid 'since' timestamp format. Use ISO8601." }Invalid timestamp format
400{ "error": "Invalid fields: ... Valid fields: ..." }One or more fields values are not recognized
401{ "error": "Unauthorized" }Invalid or missing secret key
403{ "error": "API access requires a Pro plan..." }Organization does not have API access
404{ "error": "Motion not found" }Motion does not exist or belongs to different organization

Limits

  • Maximum 100 items per page (requests for more are capped automatically)
  • Default page size is 50 items
  • The API is designed for polling at minute intervals or less frequently