@readdle/use-spark

>-

View in AI SkillSafe app
Scanned · no findings
0 downloads
0 stars
0 demos
SKILL.md
nameuse-spark
description>-
metadataspark

Using spark

spark is a CLI for the Spark email client. Use it to query the user's mailbox, calendar, contacts, meetings, and team data.

Running spark

spark <command> [options]

Environment: spark is a thin client that talks over IPC to the user's running Spark Desktop app - it does not ship its own mailbox, network stack, or credentials. Run it directly on the user's computer against the live Spark Desktop process. Do not try to execute it inside a sandbox, container, CI runner, or any environment isolated from the user's desktop session - it will fail to connect. If Spark Desktop is not running, ask the user to launch it instead of retrying.

Commands

Command Description
accounts List accounts, calendars, teams, shared inboxes, and access levels
folders List folders/labels with message counts
emails List emails with filters and pagination
search Hybrid keyword + semantic search with full bodies
thread Read full thread - headers, bodies, attachments
attachment Read a single email attachment by its ID (auto-downloads)
draft Create, edit, or delete an email draft (new, reply, forward, from template), or list account signatures
templates List saved message templates (personal and team)
template Show a single template by ID or name with its placeholders
comment Post a team comment on a thread
events List calendar events for a time range
event Create, update, delete, or RSVP to a calendar event, including managing attendees / invitations
availability Find free time slots, optionally with attendees
contacts Search contacts by name or email
team Show team info, members, shared inboxes, assignments
meetings List meeting transcripts
meeting Read a single meeting transcript
action Perform actions on emails (archive, pin, snooze, assign, etc.)
contact-action Perform actions on contacts (block, accept, categorize, etc.)

accounts

List all configured accounts with their aliases, calendars, teams, and shared inboxes. Each account and shared inbox shows its access level in parentheses, which controls what operations Spark can perform.

spark accounts

Run this first to discover what accounts, calendars, and teams are available, and to check their access levels.

Access levels:

Level Allowed operations
read-only List, search, and read emails, threads, folders, events, contacts, meetings, teams
triage Everything in read-only plus all write operations: drafts, team comments, email actions (archive, move, pin, snooze, assign, etc.), contact actions (block, accept, categorize, etc.)
send Everything in triage plus mail-emitting operations: sending drafts (action send, including scheduled "Send Later"), and the entire event command: event create / event update / event delete / event rsvp, including attaching or detaching attendees via --add / --remove and the iTIP REQUEST / UPDATE / CANCEL / REPLY mail that goes with them.

Access levels are configured separately for each account and each shared inbox in Spark Desktop under Settings -> AI Agents. Shared inboxes can have a different access level than the parent account - for example, a personal account may have triage access while a shared inbox under the same team is read-only or disabled.

If a command requires a higher access level than the account or shared inbox has, it returns an error with instructions on how to change the level.

folders

List folders with message counts. Output includes folder identifiers in parentheses - use these as arguments to emails and search. Mailboxes backed by a Google account show (Gmail labels) on the Email Account or Shared Inbox header. Teams show the team name as a usable identifier for emails.

spark folders                        # all accounts
spark folders [email protected]       # single account

emails

List emails with metadata (ID, From, Date, Subject, Flags). Supports pagination and Gmail-style filters.

spark emails                                                   # Unified Inbox
spark emails [email protected]:Archive                          # specific folder
spark emails "My Team"                                         # all shared threads in a team
spark emails --filter "from:[email protected] is:unread"             # filtered
spark emails --filter "newer_than:7d has:attachment"            # recent with attachments
spark emails --page 2 --page-size 20                           # pagination
spark emails --order ascending                                 # oldest first
spark emails --new-senders                                     # show only new sender emails

GateKeeper filtering: When viewing the Inbox with GateKeeper in explicit mode, new sender emails are automatically filtered out and a "New Senders" count is shown at the top. Use --new-senders to view those emails. Use contact-action acceptContact <email> or contact-action blockContact <email> to accept or block a sender.

Folder identifier formats (run folders to see available ones):

Format Example Meaning
Bare name Inbox, Archive Unified folder (cross-account)
email [email protected] Account inbox shorthand
email:Folder [email protected]:Archive Specific account folder
"Team Name" "My Team" All shared threads in a team (quote if spaces)
shared@email:Inbox [email protected]:Inbox Shared inbox open items (conversation view, matches Desktop)
shared@email:Archive [email protected]:Archive Shared inbox done/archived items (conversation view)
shared@email:Folder [email protected]:Label Other shared inbox folder / label

Filter operators (combinable, Gmail-style):

Operator Example
from:<addr> from:[email protected]
to:<addr> to:[email protected]
cc:<addr> cc:[email protected]
subject:<text> subject:"quarterly report"
before:yyyy/MM/dd before:2026/03/01
after:yyyy/MM/dd after:2026/01/01
newer_than:Xd newer_than:7d (also w, m, y)
older_than:Xd older_than:30d
has:attachment also document, spreadsheet, presentation, reminder
is:unread also read, starred, pinned, unreplied
is:shared emails shared to any team (alias for is:shared_email)
is:shared_inbox_open open items in shared inbox
is:shared_inbox_done completed/closed items in shared inbox
category:personal also priority, notification, newsletter, invitation, invitation_response
assigned_to:me emails assigned to current user
assigned_to:<email> emails assigned to specific teammate
assigned_to:unassigned shared inbox items with no assignee
assigned_to:other emails assigned to someone else (not me)
assigned_by:me emails delegated by current user
filename:<name> filename:report.pdf

search

Two modes:

  • With a topic (keyword mode): Hybrid keyword + semantic search returning up to 20 emails with full bodies, sorted by relevance.
  • Without a topic (list mode): Paged compact table of every email matching --filter / --in across all folders and all accounts, sorted newest first. Same output as emails, but the default scope is "all folders" instead of the Unified Inbox. Trash, Spam, and Blocked are excluded (matching Spark's search field) unless --in explicitly targets one of those folders.
spark search "quarterly report"
spark search "API integration" --filter "from:[email protected]"
spark search "budget" --in [email protected]:Archive
spark search "vacation" --in [email protected]              # all folders in account

# Keywordless list mode - filter across every folder
spark search --filter "from:[email protected]"                  # every email from alice, all folders
spark search --filter "from:[email protected]" --in [email protected]
spark search --filter "is:unread newer_than:7d" --page 2
Parameter Required Description
<about> No Search topic (positional). Omit to switch to list mode.
--filter No Gmail-style filter (same operators as emails)
--in No Scope: account, team, folder, or shared inbox. All folders if omitted.
--page No Page number, 1-based (default: 1). List mode only.
--page-size No Emails per page (default: 50). List mode only.
--order No Sort order: ascending or descending. List mode only.

Use search with a topic when the user asks about content - it returns email bodies so you can answer questions. Use search without a topic when you need to filter emails (especially by from:) across every folder - emails only sees the Unified Inbox so it can't answer questions like "every email from [email protected], anywhere". Use emails for plain browsing of Inbox / one folder.

thread

Print every message in a thread - headers, full plain-text bodies, and attachment info. After the thread summary line, lists custom (non-system) folder labels once for the whole thread, using qualified names like [email protected]:MyLabel (same style as folders).

spark thread 1114                          # by message ID from emails/search output
spark thread --download-attachments 1114   # also fetch attachments via IMAP
spark thread "https://sparkmailapp.com/dpl/bl?token=ABC..."  # by Spark deep link

The positional argument accepts either a numeric message ID (the ID: line) or a Spark deep link (the Link: line) printed by a previous run - https://sparkmailapp.com/dpl/bl?token=..., readdle-spark://bl=..., or readdlespark://bl=....

A Reply-To: line appears only when that header points somewhere other than From - mailing lists and website contact forms carry the real correspondent there. draft --reply-to already addresses the reply to it, so don't pass --to yourself.

Each message's Attachments: block is a table with columns ID, Name, Size, MIME Type, and Path. The ID column is the attachment's stable pk - feed it to attachment to read the file contents (auto-downloads if necessary). The Path column shows the local file or (not downloaded, ...) for attachments not yet fetched.

Use emails or search to find message IDs (the ID column), then thread to read the full conversation. Use folders to list valid label identifiers for action attachLabel / detachLabel.

attachment

Read a single email attachment by its ID (pk) from the thread Attachments table. The file is auto-downloaded if it isn't cached locally yet.

spark attachment 42                          # print metadata (ID, Name, Size, MIME Type, Path, Message ID)
spark attachment 42 --stream > report.pdf    # write raw file bytes to stdout
Parameter Required Description
<id> Yes Attachment ID (pk) from the thread Attachments table.
--stream No Write the raw file bytes to stdout instead of metadata text. Useful inside sandboxed agents that can read the CLI's stdout but not local filesystem paths. The CLI streams the file in 64 KB chunks, so there is no practical size limit. To go the other way - attach a file the app can't read - pipe it into draft/comment with --attach-stream.

Use thread to find attachment IDs (the ID column in the Attachments: table). The default text output is one Key: value per line, easy to parse from scripts.

draft

Requires: triage access level.

Create a new email draft or edit an existing one. The body is written in markdown and converted to HTML.

spark draft --to "[email protected]" --subject "Hello" --body "Hi Alice, ..."
spark draft --to "[email protected]" --to "[email protected]" --cc "[email protected]" --subject "Meeting" --body "..."
spark draft --edit 1234 --subject "Updated subject" --body "Updated body"
spark draft --reply-to 5678 --body "Thanks for the update!"
spark draft --reply-all 5678 --body "Thanks everyone!"   # keeps the other recipients and the CCs
spark draft --forward 5678 --to "[email protected]" --body "FYI"
spark draft --account "[email protected]" --to "[email protected]" --subject "Hi" --body "..."
spark draft signatures                                                                # the signature each account appends
spark draft --to "[email protected]" --subject "Quick note" --body "..." --no-signature   # send without a signature
spark draft --edit 1234 --no-signature                                                # strip the signature from an existing draft
spark draft --to "[email protected]" --subject "Report" --body "See attached" --attach /path/to/report.pdf
spark draft --to "[email protected]" --subject "Files" --body "Two files" --attach /path/to/a.pdf --attach /path/to/b.xlsx
spark draft --reply-to 5678 --body "Resending the file" --attach-id 42   # attach a file from the original email
cat report.pdf | spark draft --to "[email protected]" --subject "Report" --body "See attached" --attach-stream report.pdf   # pipe a file the app can't read directly
spark draft --to "[email protected]" --subject "Proposal" --body "..." --team "Engineering" --user [email protected] --user [email protected]
spark draft --edit 1234 --team "Engineering" --user [email protected] --allow-send
spark draft --edit 1234 --user [email protected]           # invite carol on an already-shared draft
spark draft --edit 1234 --allow-send                  # grant send-on-behalf permission on an already-shared draft
spark draft --edit 1234 --no-allow-send               # revoke previously-granted send-on-behalf permission
spark draft --edit 1234 --remove-user [email protected]    # kick alice from a shared draft (keeps share, comments, other collaborators)
spark draft --edit 1234 --remove-user [email protected] --user [email protected]  # swap collaborators: remove alice, invite dave
spark draft --edit 1234 --unshare
spark draft --delete 1234                             # remove the draft for good (no Trash, no undo)
spark draft --template "Cold outbound v3" --to "[email protected]" --placeholder "Project name=Acme Q3" --placeholder "Deadline=Friday EOD"
spark draft --template 124 --edit 9821 --placeholder "Project name=Acme Q3" --placeholder "Deadline=Friday EOD"
Parameter Required Description
--to No Recipient address (RFC822). Repeat for multiple.
--cc No CC address. Repeat for multiple.
--bcc No BCC address. Repeat for multiple.
--subject No Subject line.
--body Yes (new, no --template) Body content in markdown. Required for new drafts unless a template provides one.
--edit No Message ID of an existing draft to update.
--reply-to No Message ID to reply to. Addresses the sender alone (or the Reply-To: address when the message carries one).
--reply-all No Message ID to reply to, keeping everyone else on the thread: the other To: recipients land in To, the original Cc: in CC, minus your own address. Mutually exclusive with --reply-to; --to / --cc override the lists it builds.
--forward No Message ID to forward.
--delete No Message ID of a draft to delete permanently. Must be the only option on the command. Drafts have no Trash, so the deletion cannot be undone. A scheduled draft and a draft shared with teammates are both refused - action unschedule or draft --edit <pk> --unshare first.
--account No Account email to send from. Accepts a regular mail account, an alias, or a shared inbox email.
--attach No Absolute path to a file to attach. Repeat for multiple. The Spark app must be able to read the path; in the sandboxed App Store build a path outside the app's container can't be read and is rejected with a clear error - pipe the file with --attach-stream instead. Max 25 MB per file.
--attach-id No ID of an attachment on an existing email to copy onto this draft, from the Attachments table of thread <message-id>. Repeat for multiple. Use this to re-send a file the user received - replies don't inherit attachments (only --forward does).
--attach-stream No Attach a single file whose bytes are read from stdin, shown to recipients as <name>. Use this when the file is outside the app's sandbox (the App Store build can't read arbitrary paths) - it's the inbound twin of attachment --stream. One streamed file per command; combine with --attach for paths the app can read. Max 25 MB. Example: cat report.pdf | spark draft --edit 123 --attach-stream report.pdf. Needs a real stdin, so it only works when you run the spark binary yourself - otherwise use --attach-id / --attach.
--team No Team name. Required when you belong to multiple teams. When editing a draft that's already shared, must match the team that owns the share.
--user No Teammate email to share with. Repeat for multiple. On an already-shared draft this adds collaborators without removing existing ones - use --remove-user to remove someone.
--remove-user No Teammate email to remove from an already-shared draft. Repeat for multiple. The shared draft, its comments, and the remaining collaborators are preserved (unlike --unshare, which tears the whole share down). Requires --edit <shared-pk>. Cannot remove yourself - use --unshare for that. Can be combined with --user in one command to swap collaborators; removals run before invites.
--allow-send No Grant teammates permission to send the shared draft on your behalf. New share: defaults to off when omitted. Edit of a shared draft: leaves the current value alone when omitted. Mutually exclusive with --no-allow-send.
--no-allow-send No Revoke teammates' permission to send the shared draft on your behalf. Useful when editing a shared draft whose allow-send is currently on. Mutually exclusive with --allow-send.
--unshare No Revert an already-shared draft back to a personal draft. Requires --edit and is mutually exclusive with --team / --user / --remove-user / --allow-send / --no-allow-send and with content edits (--to / --cc / --bcc / --subject / --body / --attach / --attach-id / --attach-stream) - issue the edit (or per-user removal) and the unshare as separate commands.
--template No Apply a saved template by ID or name. Combine with --edit to overlay onto an existing draft.
--placeholder When template has manual placeholders Fill a manual template placeholder, format "<name>=<value>". Repeat for each. Auto-fillable placeholders (recipient/self names) are not addressable here - control them via --to and --account.
--no-signature No Send without a signature. Suppresses the account's per-mailbox default signature for this draft. On --edit it strips a signature already on the draft (the body and quoted thread are kept). Omit the flag to keep using the account default.
signatures No Positional, not a flag: prints every mailbox you can draft from with the signature it appends. Must be the only argument.

Explicit flags always win over template fields. Use template <id|name> to discover the template's manual placeholders before calling draft --template - missing manual placeholders cause a hard error before any draft is created. Auto-fillable placeholders that fail to resolve (e.g. recipient name with multiple --to) leave a localized label in the body and surface in the response as a warning.

The signature is added for you - never write your own sign-off. Spark appends the mailbox's default signature to the body of every draft. spark draft signatures prints what each mailbox you can draft from appends.

When the user asks for a closing that differs from their signature, pass --no-signature and write the whole closing yourself.

On success the output includes the draft's ID: (use it with --edit and action send) and a Link: line with a Spark deep link (https://sparkmailapp.com/dpl/bl?token=...) that opens the draft directly in Spark. It also echoes the composed body under Body: - the whole message as the recipient will read it, signature included and quoted thread left out. Read it back to confirm the draft says what you meant, and to catch a sign-off of your own standing next to the account's.

Always give the user the deep link. After creating or updating a draft, include the Link: URL in your response as a clickable markdown link (e.g. [Open draft in Spark](https://sparkmailapp.com/dpl/bl?token=...)) so the user can jump straight to the draft to review or send it. Do not tell the user to open Spark and hunt for the draft manually.

Use emails to find message IDs for --edit, --reply-to, --reply-all, and --forward. Use accounts to find account emails for --account - personal accounts, their Alias: entries, and shared inboxes are all listed there, and any of them can be used as the from address when the account has draft & comment access. Use teams to find team names for --team and team member emails for --user.

Threading is critical. Whenever a new message belongs to an existing conversation, you must pass --reply-to with the last message in that thread. This is what attaches the draft to the conversation (correct In-Reply-To / References headers, same thread in the recipient's mailbox). Without --reply-to the draft starts a brand new thread, which is almost always wrong when the user asked you to "reply", "respond", "follow up", "answer", or "ping" anyone in the context of an existing conversation. Use thread <id> to inspect the conversation and pick the most recent message's ID as --reply-to. On a thread with several participants, use --reply-all unless the user wants a private answer to the sender.

Follow-ups (no response yet). When the user asks to follow up with someone you already emailed and they haven't replied yet (e.g. "send Alice a nudge - she never responded to my last email", "bump the proposal thread"), the most recent message in that thread is your own outgoing one. Use that message's ID as --reply-to - the follow-up stays attached to the original outgoing message so the recipient sees it as a bump on the existing conversation rather than a new cold email.

Sharing is triggered by the presence of --team or --user; teams with exactly one other active member auto-share with everyone, otherwise --user is required. To add collaborators or change the allow-send setting on an existing shared draft, use --edit <pk> together with the sharing flags - the change is applied to the existing share instead of creating a new one. To toggle allow-send off, pass --no-allow-send. To remove a specific collaborator without tearing the share down, pass --remove-user <email>; the shared draft, its comments, and the remaining collaborators stay intact. Combine --user and --remove-user in one command to swap collaborators in a single operation - removals run before invites. Content edits (--to, --cc, --bcc, --subject, --body, --attach) and sharing updates (--team, --user, --remove-user, --allow-send, --no-allow-send) must be issued as separate draft commands.

Deleting a draft is final. --delete <pk> removes the draft outright - unlike a received email there is no Trash to recover it from, and neither history --undo nor Spark's own undo can bring it back. Only delete a draft the user asked you to discard, and say so plainly in your response rather than implying it can be restored. To throw away just the text of a draft while keeping the draft itself, edit it (draft --edit <pk> --body "...") instead.

templates

List Spark message templates - the saved drafts users can apply via draft --template. Templates round-trip from desktop, so anything saved on the user's computer shows up here.

spark templates                          # all personal + team templates
spark templates --personal               # only personal templates
spark templates --team "Marketing"       # only that team's templates
spark templates --page 2 --page-size 20  # pagination

Output columns: ID, Scope (Personal / <team name>), Name, Subject (truncated to 40 chars), Modified. Use the ID or Name value with template <id|name> and draft --template.

template

Read-only. Show a single template's full contents and its placeholder requirements. Run this before draft --template so you know which --placeholder "<name>=<value>" arguments the template needs.

spark template 123                       # by ID
spark template "Welcome reply"           # by name (case-insensitive)

Output includes scope, recipients, subject, body (HTML stripped to text), attachments, and a Placeholders: section listing every placeholder in the template:

  • [auto] - auto-fillable (recipient/self name). Resolved from --to and --account when applied. Not overridable via --placeholder.
  • [manual] - free-form placeholder. Required: must be passed as --placeholder "<name>=<value>" to draft --template.

If a name matches more than one template, you'll get an error listing the matching IDs - disambiguate by ID.

comment

Requires: triage access level.

Post a team comment (chat message) on a thread. If the thread is not yet shared, it will be shared automatically. Supports text comments, file attachments, or both. Use --edit to update an existing comment.

spark comment 1234 --body "Looks good, let's proceed."
spark comment 1234 --body "Please review this" --team "Engineering"
spark comment 1234 --body "FYI" --team "Engineering" --user [email protected] --user [email protected]
spark comment 1234 --attach /path/to/screenshot.png
spark comment 1234 --body "See attached" --attach /path/to/report.pdf --attach /path/to/data.csv
cat screenshot.png | spark comment 1234 --attach-stream screenshot.png   # pipe a file the app can't read directly
spark comment --edit 5678 --body "Updated comment text"
Parameter Required Description
<message-id> Yes (post) Message ID of a message in the thread to comment on.
--body When no --attach Comment text to post. Required when using --edit.
--attach When no --body Absolute path to a file to attach. Repeat for multiple files. Each file is sent as a separate message. Cannot be used with --edit. The Spark app must be able to read the path; in the sandboxed App Store build a path outside the app's container can't be read and is rejected with a clear error - pipe the file with --attach-stream instead. Max 25 MB per file.
--attach-stream When no --body Attach a single file whose bytes are read from stdin, shown as <name>, sent as its own comment message. Use this when the file is outside the app's sandbox (the App Store build can't read arbitrary paths) - it's the inbound twin of attachment --stream. One streamed file per command; cannot be used with --edit. Max 25 MB. Example: cat shot.png | spark comment 456 --attach-stream shot.png --team "Engineering". Needs a real stdin, so it only works when you run the spark binary yourself - otherwise use --attach.
--edit No Message ID of an existing comment to edit. Requires --body.
--team When >1 team Team name. Required when you belong to multiple teams.
--user When team >2 members Teammate email to share with. Repeat for multiple. Only used when auto-sharing an unshared thread. For teams with 2 or fewer members, the whole team is shared with automatically.

Use emails to find message IDs. Use thread to see comment IDs in a conversation. Use team to list teams and their members.

events

List calendar events for a time range.

spark events                                          # today's remaining events
spark events --tomorrow
spark events --week
spark events --week --in [email protected]             # specific account
spark events --week --in [email protected]:Work        # specific calendar
spark events --start 2026-03-16 --end 2026-03-20      # custom range

Date formats: yyyy-MM-dd, dd/MM/yyyy, or yyyy-MM-ddTHH:mm.

Run accounts to see available calendar accounts and calendar names.

event

Requires: send access level on the target calendar's owning account. Every mode can emit mail through the calendar service (invitations on create / update, iTIP UPDATE / CANCEL on update / delete of attendee-bearing events, an iTIP REPLY on rsvp), and attaching attendees makes the event discoverable by the provider's invitation channel independent of email, so the whole command sits at send.

Create, update, delete, or RSVP to a calendar event, including managing its attendees. Use --add / --remove on create or update to attach or detach attendees and send invitations / cancellations through the calendar provider (CalDAV / Google / Exchange).

spark event create --title "Sync" --start 2026-07-01T12:00 --end 2026-07-01T13:00
spark event create --title "OOO" --start 2026-07-01 --all-day
spark event create --title "Standup" --start 2026-07-01T09:00 --end 2026-07-01T09:15 --video-conference auto
spark event create --title "Sync" --start 2026-07-01T10:00 --end 2026-07-01T10:30 --video-conference zoom
spark event create --title "1:1" --start 2026-07-01T14:00 --end 2026-07-01T14:30 --calendar "[email protected]:Work"
spark event create --title "Sync" --start 2026-07-01T12:00 --add "[email protected],[email protected]"   # create + invite
spark event update ABC-123 --title "New title"
spark event update ABC-123 --location "Room 7"
spark event update ABC-123 --video-conference meet   # add a meeting link
spark event update ABC-123 --add [email protected] --remove [email protected]   # swap attendees
spark event delete ABC-123
spark event rsvp ABC-123 accept     # calendar event ID
spark event rsvp ABC-123 decline
spark event rsvp 44268 maybe        # invitation email message ID
Parameter Required Description
mode Yes create, update, delete, or rsvp (positional).
event_id For update/delete/rsvp Positional, second arg. For update/delete: a calendar event ID (use events). For rsvp: either a calendar event ID or the message ID of the invitation email (use emails / thread).
status For rsvp RSVP status (positional, third arg): accept, decline, or maybe.
--title No Event title / summary.
--start Yes (for create) Start date/time (yyyy-MM-dd, dd/MM/yyyy, yyyy-MM-ddTHH:mm, or yyyy-MM-ddTHH:mm:ssXXX).
--end No End date/time.
--all-day No Mark the event as all-day.
--description No Event description / notes.
--alerts No Comma-separated alert offsets (Ns for N seconds before, e.g. 300s,600s) or absolute dates.
--location No Event location text.
--video-conference No Attach a video conference (value required). auto auto-picks the account's type (recently used, else default); meet, zoom, or teams selects a specific one and errors if the account doesn't support it.
--calendar No Target calendar for create. Format: [email protected] (account's default calendar) or [email protected]:Name (specific named calendar).
--add No Attendee email address(es) to invite (create / update only). Repeat the flag or pass a comma-separated list. New attendees receive an iTIP REQUEST (invitation).
--remove No Attendee email address(es) to remove (update only). Repeat the flag or pass a comma-separated list. Removed attendees receive an iTIP CANCEL. The organizer cannot be removed.

Attendees (--add / --remove):

  • Only valid on create and update; passing them to delete or rsvp is refused. Combine both in one update to swap attendees - removals run before additions.
  • Adding or removing attendees always emits iTIP mail (REQUEST / CANCEL). An event with no attendees stays invisible to the calendar service's invitation channel.
  • Attaching attendees makes the event discoverable by the calendar provider's invitation channel (including Google Calendar's same-provider auto-add behavior), independent of email delivery - which is why the whole command requires send.

event update semantics:

  • --video-conference attaches a meeting link (meet / zoom / teams, or auto) to an existing event, generated through the same path as create. On an event with attendees this is a shared change, so it sends the iTIP UPDATE.
  • Event with no attendees → local update, no mail goes out.
  • Event with attendees → update + iTIP UPDATE sent to all attendees so their calendars reflect the new state. Output reports the attendee count.
  • An alerts-only edit is a personal reminder and notifies nobody, even on an event with attendees.

event delete semantics:

  • Event with no attendees → local delete, no mail goes out.
  • Event with attendees → delete + iTIP CANCEL sent to all attendees so their calendars get cleaned up too. Output reports the attendee count. Deleting without the cancellation would orphan the invitation on attendees' calendars (most visibly on Google's same-provider auto-add channel).

event rsvp semantics:

  • Sets your own attending status on an invitation: accept, decline, or maybe (alias for tentative). Responding emits an iTIP REPLY back to the organizer.
  • The id can be either a calendar event ID or the message ID of the invitation email. Many providers (Google included) leave an unanswered invite sitting in the inbox without adding it to the calendar, so when the user asks you to respond to an invitation, look it up with emails / thread and pass that message ID - you do not need a calendar event. When the invite has been auto-added to the calendar, a calendar event ID works too.
  • For a message ID, the reply mirrors Spark Desktop: it updates the matching calendar event if one is synced, otherwise it mails the iTIP REPLY straight to the organizer (and, on accept, adds the event to your calendar).
  • Errors if the target is not an invitation you can respond to (a plain email, your own organized event with no RSVP, or a provider that does not allow responding).
  • For a calendar event, re-RSVPing to the status you already hold is a no-op and reported as such.

Typical flow when scheduling a new meeting: call event create ... to lay down the event first, confirm the time and details with the user, then call event update <event-id> --add ... to push invitations - so the user can review (or you can revise) the event before any external email goes out. When you already have the attendees, event create ... --add ... does both in one step.

Run accounts to see writable calendars and the access level on each account.

availability

Find free time slots. Without --attendees, shows the user's own availability. With --attendees, computes mutual free windows.

spark availability                                                        # today
spark availability --tomorrow
spark availability --week --attendees [email protected]
spark availability --start 2026-03-16 --end 2026-03-20 --attendees [email protected],[email protected]

Free slots are within working hours (08:00-20:00), skip weekends, and ignore events marked "free".

contacts

Search contacts by name or email. Strict match first, then fuzzy fallback.

spark contacts "john"
spark contacts "example.com"

team

Show team info - metadata, shared inboxes with members, full member list, assigned emails, assignment summary.

spark team                     # list available teams
spark team "Readdle"           # specific team

meetings

List meeting transcripts with optional filters and pagination.

spark meetings
spark meetings --filter "newer_than:30d"
spark meetings --filter "subject:standup" --page-size 10

Filter operators: subject:<text>, before:yyyy/MM/dd, after:yyyy/MM/dd, newer_than:Xd, older_than:Xd.

meeting

Read a single meeting transcript's summary. Optionally include the full transcript and/or notes.

spark meeting 42                            # summary only
spark meeting --transcript 42               # include transcript
spark meeting --notes 42                    # include notes
spark meeting --transcript --notes 42       # everything
spark meeting "https://sparkmailapp.com/dpl/bl?token=ABC..."  # by Spark deep link

The positional argument accepts either a numeric meeting message ID or a Spark deep link.

Use meetings to find meeting IDs.

action

Requires: triage access level.

Perform an action on one or more emails. Supports standard email actions and team actions.

spark action <action-name> <message-id...> [options]

Supported actions:

  • pin - Pin the message to keep it at the top of the list
  • unpin - Remove the pin from the message
  • mute - Mute the thread to stop receiving notifications
  • unmute - Unmute a previously muted thread
  • snooze - Snooze the message until a specific date (requires --date)
  • unsnooze - Remove the snooze and return the message to the inbox
  • changeReminder - Set a follow-up reminder if no reply by the date (requires --date)
  • clearReminder - Remove the follow-up reminder from the message
  • setAside - Set the message aside for later review
  • archive - Archive the message, removing it from the inbox
  • moveToInbox - Move the message back to the inbox
  • moveToTrash - Move the message to trash
  • moveToFolder - Move the message to a specific folder (requires --folder)
  • attachLabel - Attach a Gmail or Spark Team label without removing other labels (requires --folder)
  • detachLabel - Remove a Gmail or Spark Team label from the message (requires --folder)
  • markAsDone - Mark the message as done
  • markAsUndone - Mark the message as not done
  • markAsSeen - Mark the message as read
  • markAsUnseen - Mark the message as unread
  • markAsSpam - Mark the message as spam
  • markThreadAsPriority - Mark the thread as priority
  • unmarkThreadAsPriority - Remove the priority mark from the thread
  • unsubscribe - Unsubscribe from the sender or mailing list
  • changeCategoryPersonal - Change the email category to Personal
  • changeCategoryNotification - Change the email category to Notification
  • changeCategoryNewsletters - Change the email category to Newsletter
  • shareInTeam - Share the thread with teammates (requires --team when multiple teams)
  • assign - Assign the email to a teammate (requires --assignee)
  • delegationComplete - Mark the delegation as complete
  • delegationReopen - Reopen a completed delegation

send verb (requires send access):

Sends an existing draft (created with spark draft or in Spark Desktop) through the owning account's outbound mail pipeline. Same validation as the desktop composer: the draft must live on an account whose access level is send (Settings -> AI Agents), have at least one recipient, a non-empty subject, all attachments uploaded, and no unresolved manual template placeholders.

spark action send 1234

Output reports the original draft ID and the PK of the sent message:

Draft 1234 sent. Sent message ID: 5678.

Typical flow: spark draft ... to compose, review the draft, then spark action send <pk> to commit. Splitting the two operations means you (or the user) can edit the draft before any external mail is emitted - spark draft alone never sends.

send accepts multiple draft IDs in one call (each runs through validation independently and the response reports per-draft success/failure), but the common case is a single id.

Scheduled send ("Send Later"): pass an optional --date to schedule the draft instead of sending it now. The date must be in the future (formats: yyyy-MM-dd, dd/MM/yyyy, yyyy-MM-ddTHH:mm); a past or malformed date is rejected before anything is sent.

spark action send 1234 --date 2026-04-10T09:00

Output confirms the schedule: Draft 1234 scheduled to send on 2026-04-10 09:00. Message ID: 5678. The message sits in the outbox and is delivered at that time.

Manage an already-scheduled ("Send Later") message by targeting its message ID with send:

  • Reschedule: send with a new --date updates the existing schedule (it is no longer a plain draft, so this does not create a new message):
spark action send 5678 --date 2026-04-12T15:00

Output: Message 5678 rescheduled to send on 2026-04-12 15:00. Message ID: 5678.

  • Send now: send with no --date drops the schedule and sends it immediately:
spark action send 5678

Output: Scheduled message 5678 sent now. Message ID: 5678.

  • Unschedule: unschedule cancels the schedule and returns the message to drafts (so you can edit it or send it later):
spark action unschedule 5678

Output: Message 5678 unscheduled - returned to drafts. Find it in the Drafts folder. Clearing the schedule mints a fresh draft, so the original ID (5678) no longer resolves - re-list the Drafts folder to get the new draft ID before editing or sending it.

Both send (with or without --date) on a scheduled message and unschedule require send access on the owning account.

Options:

  • --date - required for snooze and changeReminder, optional for assign as due date (formats: yyyy-MM-dd, dd/MM/yyyy, yyyy-MM-ddTHH:mm)
  • --folder - required for moveToFolder, attachLabel, and detachLabel (qualified name: [email protected]:FolderName; use folders to list labels including shared inboxes)
  • --team - team name for team actions; required when you belong to multiple teams
  • --user - teammate email to share with for shareInTeam; repeat for multiple users; required when team has more than 2 members
  • --assignee - teammate email to assign the email to for assign
  • --comment - comment text for the assign action
spark action pin 12345                                          # pin a message
spark action archive 100 200 300                                # archive multiple messages
spark action markAsSeen 100 200                                 # mark as read
spark action snooze 12345 --date 2026-04-10T09:00               # snooze until date/time
spark action snooze 12345 --date 2026-04-10                     # snooze until date
spark action moveToFolder 12345 --folder "[email protected]:Archive"  # move to folder
spark action attachLabel 12345 --folder "[email protected]:MyLabel"       # add Gmail/Team label
spark action detachLabel 12345 --folder "[email protected]:SomeLabel"  # remove label
spark action changeReminder 12345 --date 2026-04-15             # set reminder
spark action shareInTeam 1234 --team "Engineering" --user [email protected]  # share with teammate
spark action shareInTeam 1234 --user [email protected] --user [email protected]    # share with multiple
spark action assign 1234 --assignee [email protected]                  # assign to teammate
spark action assign 1234 --assignee [email protected] --date 2026-04-15 --comment "Please review"
spark action delegationComplete 1234                            # mark delegation done
spark action delegationComplete 100 200 300                     # complete multiple delegations
spark action delegationReopen 1234                              # reopen delegation

Use the emails command to find message IDs. Use folders to resolve qualified names for moveToFolder, attachLabel, and detachLabel. Use team to list teams and members for team actions.

contact-action

Requires: triage access level.

Perform an action on one or more contacts by email address.

spark contact-action <action-name> <email...>

Supported actions:

Action Description
changeCategoryPersonal Change the contact's email category to Personal
changeCategoryNotification Change the contact's email category to Notification
changeCategoryNewsletters Change the contact's email category to Newsletter
groupEmailsFromContact Group emails from the contact by category
groupEmailsFromContactAndShowInInbox Group emails from the contact and show in inbox
ungroupEmailsFromContact Ungroup emails from the contact
markContactAsImportant Enable notifications for the contact
unmarkContactAsImportant Disable notifications for the contact
markContactAsPrimary Mark the contact as priority (auto-prioritize emails)
unmarkContactAsPrimary Remove priority mark from the contact
acceptContact Accept or unblock the contact (bypass Gatekeeper)
blockContact Block the contact
acceptDomain Accept or unblock the contact's entire domain
blockDomain Block the contact's entire domain
enableAutosummaryForContact Enable auto-summary for emails from the contact
disableAutosummaryForContact Disable auto-summary for emails from the contact

Examples:

spark contact-action blockContact [email protected]
spark contact-action acceptContact [email protected] [email protected]
spark contact-action changeCategoryPersonal [email protected]
spark contact-action markContactAsPrimary [email protected]
spark contact-action enableAutosummaryForContact [email protected]

Use the contacts command to look up email addresses.

Smart Categories

Spark automatically classifies incoming email into six categories. Use the category: filter operator with emails and search to view mail by category, and use action / contact-action to reclassify and tune.

Category Filter Typical Content
Priority category:priority Auto-prioritized or manually marked as priority
People category:personal Direct person-to-person email
Notifications category:notification Service notifications, alerts, receipts
Newsletters category:newsletter Subscriptions, digests, marketing
Invites category:invitation Calendar invitations
Invite Responses category:invitation_response RSVPs, accepts, declines

Browse by category (read-only):

spark emails Inbox --filter "category:priority is:unread"       # unread priority mail
spark emails Inbox --filter "category:personal is:unread"       # unread people mail
spark emails Inbox --filter "category:invitation"               # pending invites
spark emails Inbox --filter "category:notification is:unread"   # unread notifications
spark emails Inbox --filter "category:newsletter newer_than:7d" # recent newsletters

Reclassify a message (triage):

spark action changeCategoryPersonal <id>       # move to People
spark action changeCategoryNotification <id>   # move to Notifications
spark action changeCategoryNewsletters <id>    # move to Newsletters

Tune per-contact category rules (triage) - changes apply to all future mail from the sender:

spark contact-action changeCategoryPersonal [email protected]         # reclassify as People
spark contact-action changeCategoryNewsletters [email protected]      # reclassify as Newsletters
spark contact-action groupEmailsFromContact [email protected]         # group by category
spark contact-action markContactAsImportant [email protected]            # enable notifications
spark contact-action markContactAsPrimary [email protected]              # auto-prioritize

Embed badges

Add these to your README to show the skill's verification status.

SkillSafe verified badge
Verified badge
[![SkillSafe verified badge](https://api.skillsafe.ai/v1/badge/@readdle/use-spark/verified)](https://skillsafe.ai/skill/@readdle/use-spark/)
Installs badge
Installs badge
[![Installs badge](https://api.skillsafe.ai/v1/badge/@readdle/use-spark/installs)](https://skillsafe.ai/skill/@readdle/use-spark/)
Scan badge
Scan badge
[![Scan badge](https://api.skillsafe.ai/v1/badge/@readdle/use-spark/scan)](https://skillsafe.ai/skill/@readdle/use-spark/)
Eval pass rate badge
Eval pass rate
[![Eval pass rate badge](https://api.skillsafe.ai/v1/badge/@readdle/use-spark/eval)](https://skillsafe.ai/skill/@readdle/use-spark/)