Skip to main content

How to keep Sally tasks in sync with your system

Sally derives tasks from your conversations. This guide shows how to read them, move them into the system your team actually uses and write back when a task is done, with a complete sync script at the end.

Every example works as it stands once SALLY_TOKEN and DIRECTORY_ID are set, see the Quickstart. Pick your language once, every example on the API pages follows.

Quick navigation

  1. Read tasks
  2. Search tasks
  3. Create a task
  4. Change a task
  5. Complete or reopen a task
  6. Done is not canceled
  7. Complete sync script
  8. Endpoints at a glance

1. Read tasks​

GET/v1.0/directories/{directoryId}/tasks returns the tasks of a company account, paged. It takes these query parameters:

ParameterTypeDefaultDescription
filterstringPreset that combines scope and state, see below.
searchstringCase-insensitive substring match on the task subject.
pageinteger1Page number, starting at 1.
pageSizeinteger25Entries per page, at most 100.
fieldsstringComma-separated list of the fields to return, see Field selection.

The filter preset combines scope and state: my… covers every task you are involved in, as the responsible person or as the owner, all… every task of the company account you may see. The state part is Tasks (any), Open, Overdue, Completed or Forwarded, for example myOpen or allOverdue. Forwarded is the one exception to that scope: it lists only what you handed to someone else, so you are the owner and someone else is responsible. The all… presets are reserved for admins and owners.

ScopeAnyOpenOverdueCompletedForwarded
Yours (responsible or owner)myTasksmyOpenmyOverduemyCompletedmyForwarded
All (admin, owner)allTasksallOpenallOverdueallCompletedallForwarded

This request fetches your open tasks:

curl "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/tasks?filter=myOpen&pageSize=100" \
-H "Authorization: Bearer $SALLY_TOKEN"
200 OK
{
"page": 1,
"pageSize": 100,
"total": 137,
"hasMore": true,
"items": [
{
"taskId": "6ceba541-fa71-44c7-a7f7-644d56b1b85d",
"subject": "Quarterly planning",
"description": "Prepare the budget figures for the Q4 review.",
"ownerId": "e51b6a79-43bf-494f-a264-6825f41b0fb5",
"responsibleUserId": "1b84577d-c09e-446b-a5b8-71591dfde929",
"taskCategoryId": "4f2c8a1e-7b3d-4e9a-b6c5-2d8f1a7e3c90",
"dueDate": "2026-09-30T00:00:00Z",
"isDone": false,
"doneOn": null,
"isCanceled": false,
"canceledOn": null,
"recordingSummaryId": "731180f9-257a-46cf-a7e3-8ca508fb9c35",
"appointmentId": "d4398f97-cd92-4d21-a6ec-4aab0a85441b",
"createdOn": "2026-09-24T09:30:00Z",
"modifiedOn": "2026-09-24T09:30:00Z"
}
]
}

Every task has these fields:

FieldTypeMeaning
taskIdstringId of the task.
subjectstringTask subject.
descriptionstring or nullTask description, null if unset.
ownerIdstringId of the user who owns (created) the task.
responsibleUserIdstring or nullId of the responsible user, null if unassigned.
taskCategoryIdstring or nullId of the task category, null if the task has none. See Resolve the task category.
dueDatestring or nullDue date (ISO 8601), null if no due date is set.
isDonebooleanWhether the task is marked as done.
doneOnstring or nullWhen the task was completed (ISO 8601), null if not done.
isCanceledbooleanWhether the task is canceled.
canceledOnstring or nullWhen the task was canceled (ISO 8601), null if not canceled.
recordingSummaryIdstring or nullSummary this task originated from, null if none.
appointmentIdstring or nullAppointment this task is linked to, null if none.
createdOnstringCreation timestamp (ISO 8601).
modifiedOnstringLast modification timestamp (ISO 8601).

A task knows where it came from: recordingSummaryId and appointmentId point back to the meeting. That lets you show the meeting context next to the task in your own tool.

Recommendation

ownerId and responsibleUserId are plain ids. Resolve them to names and email addresses with GET /v1.0/directories/{directoryId}/memberships/users/{userId}. The loop from Fetch all pages fetches every page.

Resolve the task category​

taskCategoryId is a plain id as well. GET/v1.0/directories/{directoryId}/task-categories returns every category a task can carry: the ones your company account owns plus the global Sally defaults (isGlobal: true), sorted by name. The list is not paged, items holds all of them.

curl "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/task-categories" \
-H "Authorization: Bearer $SALLY_TOKEN"
200 OK
{
"items": [
{
"taskCategoryId": "4f2c8a1e-7b3d-4e9a-b6c5-2d8f1a7e3c90",
"name": "Customer follow-up",
"description": "Calls, emails and offers a customer is waiting for after a meeting.",
"active": true,
"isGlobal": false
},
{
"taskCategoryId": "9a3e5c71-2b8d-4f60-a1c4-7e9d0b2f5a38",
"name": "Internal",
"description": null,
"active": true,
"isGlobal": true
},
{
"taskCategoryId": "c81f4d26-5e0a-4b93-8d7c-3a6b9e1f0d54",
"name": "Trade fair 2025",
"description": "Preparation and follow-up of the 2025 trade fair.",
"active": false,
"isGlobal": false
}
]
}
FieldTypeMeaning
taskCategoryIdstringId of the category, the value a task carries in taskCategoryId.
namestringCategory name.
descriptionstring or nullWhat the category covers. Sally is given this text when it picks a category for a recognized task. null if unset.
activebooleanWhether the category is still offered for new tasks.
isGlobalbooleantrue for a global Sally default, false for a category your company account owns.

Deactivated categories stay in the list with active: false, so a task that still carries one keeps resolving. They are no longer offered for new tasks, so only offer the active ones when your tool lets people pick a category.

2. Search tasks​

POST/v1.0/directories/{directoryId}/tasks/search takes its parameters in the request body. Unlike the search query parameter of the list, it matches subject and description, and it filters by status and responsible person. Results are sorted by due date, earliest first.

FieldTypeDefaultDescription
searchstringCase-insensitive substring match on subject and description.
responsiblestringId of the responsible user. Your own id is always allowed. Another user's id, or leaving the field out (all responsible users), requires admin or owner.
statusstringopen (neither done nor canceled), completed, overdue (open and past due) or canceled. Leave it out for any status.
pageinteger1Page number, starting at 1.
pageSizeinteger25Entries per page, at most 100.

Your own user id is the userId from GET /v1.0/me. This request searches your overdue tasks that mention an offer:

curl -X POST "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/tasks/search" \
-H "Authorization: Bearer $SALLY_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"search\": \"offer\",
\"status\": \"overdue\",
\"responsible\": \"$USER_ID\"
}"

The response has the same envelope and task fields as the list in Read tasks. The matching rules are the same for every search endpoint, see How matching works.

3. Create a task​

POST/v1.0/directories/{directoryId}/tasks creates a task. You become its owner.

FieldTypeRequiredDescription
subjectstringyesTask subject, 1 to 1000 characters.
descriptionstring or nullnoTask description, at most 4000 characters. null for none.
responsibleUserIdstring or nullnoId of the responsible user. null or left out means unassigned.
taskCategoryIdstring or nullnoId of the task category from the category list. null or left out means none. An id from another company account is rejected.
dueDatestring or nullnoDue date as an ISO 8601 string. null for no due date.
curl -X POST "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/tasks" \
-H "Authorization: Bearer $SALLY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "Quarterly planning",
"description": "Prepare the budget figures for the Q4 review.",
"dueDate": "2026-09-30T00:00:00Z"
}'

The answer is the new task with all its fields:

200 OK
{
"taskId": "6ceba541-fa71-44c7-a7f7-644d56b1b85d",
"subject": "Quarterly planning",
"description": "Prepare the budget figures for the Q4 review.",
"ownerId": "e51b6a79-43bf-494f-a264-6825f41b0fb5",
"responsibleUserId": null,
"taskCategoryId": null,
"dueDate": "2026-09-30T00:00:00Z",
"isDone": false,
"doneOn": null,
"isCanceled": false,
"canceledOn": null,
"recordingSummaryId": null,
"appointmentId": null,
"createdOn": "2026-09-24T09:30:00Z",
"modifiedOn": "2026-09-24T09:30:00Z"
}

4. Change a task​

PATCH/v1.0/directories/{directoryId}/tasks/{taskId} changes a task with PATCH. Only the fields you send are changed, omitted fields keep their value, and null explicitly clears a field. You need permission to edit the task.

FieldTypeDescription
subjectstringNew subject, 1 to 1000 characters.
descriptionstring or nullNew description, at most 4000 characters. null clears it.
responsibleUserIdstring or nullNew responsible user. null unassigns the task.
taskCategoryIdstring or nullNew task category from the category list. null clears it. An id from another company account is rejected.
dueDatestring or nullNew due date as an ISO 8601 string. null clears it.

This request assigns the task, sets its category and removes its due date:

curl -X PATCH "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/tasks/$TASK_ID" \
-H "Authorization: Bearer $SALLY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"responsibleUserId": "1b84577d-c09e-446b-a5b8-71591dfde929",
"taskCategoryId": "4f2c8a1e-7b3d-4e9a-b6c5-2d8f1a7e3c90",
"dueDate": null
}'

The answer is the updated task, in the same shape as in Create a task.

5. Complete or reopen a task​

POST/v1.0/directories/{directoryId}/tasks/{taskId}/done completes or reopens a task with its own call, so a completion is always unambiguous. Send {"isDone": true} to mark the task as done and {"isDone": false} to reopen it.

curl -X POST "https://api.sally.io/v1.0/directories/$DIRECTORY_ID/tasks/$TASK_ID/done" \
-H "Authorization: Bearer $SALLY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"isDone": true}'

The answer is the updated task:

200 OK
{
"taskId": "6ceba541-fa71-44c7-a7f7-644d56b1b85d",
"subject": "Quarterly planning",
"description": "Prepare the budget figures for the Q4 review.",
"ownerId": "e51b6a79-43bf-494f-a264-6825f41b0fb5",
"responsibleUserId": "1b84577d-c09e-446b-a5b8-71591dfde929",
"taskCategoryId": "4f2c8a1e-7b3d-4e9a-b6c5-2d8f1a7e3c90",
"dueDate": "2026-09-30T00:00:00Z",
"isDone": true,
"doneOn": "2026-09-25T14:05:00Z",
"isCanceled": false,
"canceledOn": null,
"recordingSummaryId": "731180f9-257a-46cf-a7e3-8ca508fb9c35",
"appointmentId": "d4398f97-cd92-4d21-a6ec-4aab0a85441b",
"createdOn": "2026-09-24T09:30:00Z",
"modifiedOn": "2026-09-25T14:05:00Z"
}
Note

To remove a task entirely, use DELETE/v1.0/directories/{directoryId}/tasks/{taskId}. It is a soft delete that answers 204, and only the owner of the task or an admin or owner may do it.

6. Done is not canceled​

A task has two separate ways to end, and your system should keep them apart:

isDoneisCanceledStateTimestamp
truefalseFinisheddoneOn
falsetrueDroppedcanceledOn
falsefalseStill open
This means

isDone with doneOn means finished, isCanceled with canceledOn means dropped. A task that is neither is still open. If you map both onto a single "closed" state in your system, you lose that difference.

7. Complete sync script​

This script pulls all of your tasks page by page, maps each one to the three states above and hands it to your tool. Then it writes completions from your tool back to Sally. Replace the two functions marked your tool with calls to your own system. On a 429 from the rate limit it waits a little longer each time and repeats the same request.

#!/usr/bin/env bash
# Needs SALLY_TOKEN and DIRECTORY_ID, plus jq.
set -euo pipefail
BASE="https://api.sally.io/v1.0/directories/$DIRECTORY_ID"

# 1. Pull all of your tasks and print id, state and subject.
page=1
while : ; do
body=$(curl -s "$BASE/tasks?filter=myTasks&page=$page&pageSize=100" \
-H "Authorization: Bearer $SALLY_TOKEN")
echo "$body" | jq -r '.items[]
| [.taskId, (if .isDone then "done" elif .isCanceled then "canceled" else "open" end), .subject]
| @tsv'
[ "$(echo "$body" | jq '.hasMore')" = "true" ] || break
page=$((page + 1))
done

# 2. Write completions back. COMPLETED_IDS holds the ids your tool reports as done.
for task_id in ${COMPLETED_IDS:-}; do
curl -s -X POST "$BASE/tasks/$task_id/done" \
-H "Authorization: Bearer $SALLY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"isDone": true}' > /dev/null
done
Recommendation

Store the Sally taskId in your tool as the external id. Then each run updates the same record instead of creating a duplicate, and you know which id to send back when a task is closed on your side.

8. Endpoints at a glance​

Each entry leads to the reference, where you can send the request with your own token.