Slack List -> Things3 Sync
slingsync pulls Slack List items and creates Things3 to-dos.
What It Does
- Reads Slack List items using
slackLists.items.list. - Creates Things3 to-dos via AppleScript.
- Avoids duplicates using a persisted state file.
- By default, deletes previously synced Things3 to-dos if the Slack item is gone or marked completed (
--delete-missingenabled by default). - By default, runs two-way completion sync: marks Slack items completed when mapped Things to-dos are completed (
--two-way-syncenabled by default).
Requirements
- macOS with Things3 installed.
- Python 3.9+.
uvinstalled (docs) only if you useuv tool,uvx, oruv run.- Slack token with
lists:read. - For two-way completion sync, token also needs
lists:write.
Install from PyPI
pip
pip install slingsync
slingsync --help
uv tool
uv tool install slingsync
slingsync --help
Slack Setup
- Create app: api.slack.com/apps
- Add scope
lists:read - Install/reinstall app to workspace
- Copy token (
xoxb-...or user token)
IDs You Need
How to find List ID and Team ID
- Open the Slack List in your browser.
- Copy the URL. It looks like:
https://XXX.slack.com/lists/TXXX/FXXX
- Extract:
- Team ID: the
T...segment (workspace/team) - List ID: the
F...segment (the list itself)
- Team ID: the
Use in config:
- Required: set
list_idin your config JSON. - Recommended: set
team_idin your config JSON.
Config File
No repo clone is required for the global config flow.
Config lookup order:
--config /path/to/config.json$SLINGSYNC_CONFIG~/.config/slingsync/config.json
Global config
First run bootstrap:
slingsync --dry-run
If ~/.config/slingsync/config.json does not exist, the command auto-creates it with empty keys.
Then edit ~/.config/slingsync/config.json.
Then set values:
{
"slack_token": "xoxb-XXX",
"list_id": "FXXX",
"team_id": "TXXX",
"assigned_user_id": "UXXX"
}
Look up your Slack member ID (for assigned_user_id):
curl -sS -G https://slack.com/api/users.lookupByEmail \
-H "Authorization: Bearer xoxb-XXX" \
--data-urlencode "email=you@XXX.com"
Read user.id from the response and put it in your config file.
If you get missing_scope, add users:read.email (and usually users:read) then reinstall the app.
assigned_user_id is used when you pass --filter-assigned-user-id.
Common Runs
Minimal run
uv run slingsync \
--dry-run
Dry run + open items + configured assignee filter + deadline mapping
uv run slingsync \
--filter-completed false \
--filter-assigned-user-id \
--deadline-column "todo_due_date" \
--dry-run
Default behavior includes deletion reconcile (preview first)
uv run slingsync \
--filter-completed false \
--filter-assigned-user-id \
--dry-run
Remove --dry-run to apply.
Disable Two-way completion sync
uv run slingsync \
--no-two-way-sync \
--dry-run
Disable deletion reconcile
uv run slingsync \
--no-delete-missing
Argument Reference
Slack
--config: explicit config JSON path (highest priority).$SLINGSYNC_CONFIG: config JSON path env override.- Default config path:
~/.config/slingsync/config.json. - Config values:
slack_token,list_id, optionalteam_id, optionalassigned_user_id. --include-archived: Include archived Slack items.--lookup-user-id: Lookup Slack member ID by email and exit.--lookup-email: Email for--lookup-user-id.
Field Mapping And Filters
--title-column: Column key/id used as Things title.--notes-column: Column key/id used as Things notes.--deadline-column: Column key/id used for Things deadline.--completed-column: Column key/id for completion state.--assigned-column: Column key/id for assignee.--status-column: Column key/id for status.--filter-completed {true|false|open|completed|...}: include only matching completion state.--filter-assigned-user: match assignee by name substring (case-insensitive).--filter-assigned-user-id/--no-filter-assigned-user-id: enable/disable assignee filter by configured Slack member ID. If enabled and noassigned_user_idis set in the resolved config file, the script exits with an error.--filter-status: include only status value.--exclude-status: exclude status value.--debug-filter-fields: print parsed status/assignee/completed fields per item.
Things Target
--target-list: Things list destination (defaultInbox).--target-project: Destination project (mutually exclusive with area).--target-area: Destination area (mutually exclusive with project).
Execution
--dry-run: Preview only, no writes.--state-file: Custom state file path.--max-items: Process only first N fetched items.--two-way-sync/--no-two-way-sync: enable/disable pushing Things completion back to Slack (default: enabled).--delete-missing/--no-delete-missing: enable/disable deletion reconcile (default: enabled).- Reconcile deletes when a previously synced Slack item is either missing from the list or marked completed.
Safety rule:
--delete-missing(default on) cannot be used with--max-items.- If you need
--max-items, add--no-delete-missing.
State File
Default:
~/.config/slingsync/state/slack_to_things_<LIST_ID>.json
Use --state-file to override.
Quick API check:
curl -sS -X POST https://slack.com/api/slackLists.items.list \
-H "Authorization: Bearer xoxb-XXX" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{"list_id":"FXXX","team_id":"TXXX","limit":1}'
Automation Permission
First run may prompt macOS automation permission to control Things3. If denied accidentally, reset and retry:
tccutil reset AppleEvents com.apple.Terminal
(or reset iTerm2 bundle id if you use iTerm).
Metadata
Release files for slingsync 0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| slingsync-0.2.tar.gz | 14.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| slingsync-0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.8 kB
Release files / slingsync-0.2.tar.gz
| Download URL | slingsync-0.2.tar.gz |
|---|---|
| Size | 14.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60acf647e218273ce55b3b46dc4d0f7c448734f78b6d17ff4a86d2dfcdb61bb5
|
|
BLAKE2b-256 checksum How to use checksums |
fef9c054da6a3d1867dca78334ec7365902eb7f8e044a1a66d41aa97d0e1f52d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / slingsync-0.2-py3-none-any.whl
| Download URL | slingsync-0.2-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0fb3462936314c1caf6988159fcd9224df122cd81b7760c2ed6e9f7afc675fda
|
|
BLAKE2b-256 checksum How to use checksums |
008653269530e08655da429d3f1cb7ce4fbcc54f8ea44dcccec393682fbb2ae9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|