
There There
- 2 installs
- Updated August 2, 2026
- spatie/there-there-cli
Manages There There helpdesk tickets, contacts, tags, and channels from the terminal via the there-there CLI, with reply, assign, and search commands.
About
Wraps the there-there CLI so an agent can list, view, reply to, assign, and search helpdesk tickets on there-there.app from the command line. A developer uses it to triage support tickets, run bulk assignments, and search tickets semantically or by keyword.
- Every There There API endpoint maps to a CLI command, all returning JSON
- Supports semantic (--q) and full-text ticket search plus status, tag, and date filters
There There by the numbers
- 2 all-time installs (skills.sh)
- Ranked #445 of 550 CLI & Terminal skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/spatie/there-there-cli --skill there-thereAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| Last updated | August 2, 2026 |
| Repository | spatie/there-there-cli ↗ |
What it does
Manages There There helpdesk tickets, contacts, tags, and channels from the terminal via the there-there CLI, with reply, assign, and search commands.
Files
There There CLI
The there-there CLI lets you manage There There helpdesk tickets from the terminal. Every There There API endpoint has a corresponding command.
Prerequisites
Check that the CLI is installed:
there-there --versionIf not installed:
composer global require spatie/there-there-cliEnsure Composer's global bin directory is in PATH:
composer global config bin-dir --absoluteAuthentication
# Log in (automatically creates a profile named after your workspace)
there-there login
# Log in with a specific profile name
there-there login --profile=spatie
# Log out the active profile
there-there logoutGet your API token from your workspace settings at https://there-there.app/settings/account/api-tokens.
If any command returns a 401 error, the token is invalid or expired. Run there-there login again.
Profiles
The CLI supports multiple profiles for users with access to multiple workspaces.
# Log in to multiple workspaces
there-there login --profile=spatie
there-there login --profile=ohdear
# List all profiles
there-there profiles
# Switch the default profile
there-there use spatie
# Run a single command against a different profile
there-there list-tickets --profile=ohdearWhen logging in without --profile, the profile is automatically named after the workspace (slugified). All commands accept --profile to override the active profile for that invocation.
Quick command reference
All commands output JSON. See references/commands.md for full parameter details.
User & workspace
# Who am I? Returns user and workspace info
there-there get-meTickets
# List all tickets (paginated)
there-there list-tickets
# Filter by status
there-there list-tickets --filter-status=open
# Filter by channel or tags
there-there list-tickets --filter-channel-ulids=CHANNEL_ULID --filter-tag-ulids=TAG_ULID1,TAG_ULID2
# Only my tickets
there-there list-tickets --filter-assigned-to-me=true
# Sort by most recent activity
there-there list-tickets --sort=-updated_at
# View a specific ticket with messages and activities
there-there show-ticket --ticket=01HX9F3K2M...
# Semantic search (AI-powered, finds tickets by meaning)
there-there list-tickets --q="how do I get a refund"
# Full-text keyword search
there-there list-tickets --filter-search="refund"
# Filter by specific assignee
there-there list-tickets --filter-assigned-user-ulid=USER_ULID
# Filter by date range
there-there list-tickets --filter-created-after=2026-01-01 --filter-created-before=2026-02-01
# Combine search with filters
there-there list-tickets --q="billing issue" --filter-status=open --filter-created-after=2026-01-01Ticket actions
# Change status
there-there update-ticket-status --ticket=ULID --field status=closed
# Assign to a user
there-there update-ticket-assignee --ticket=ULID --field assignee_ulid=USER_ULID
# Unassign
there-there update-ticket-assignee --ticket=ULID --field assignee_ulid=null
# Assign to a team
there-there update-ticket-team --ticket=ULID --field team_ulid=TEAM_ULID
# Add a tag
there-there add-tag-to-ticket --ticket=ULID --tag=TAG_ULID
# Remove a tag
there-there remove-tag-from-ticket --ticket=ULID --tag=TAG_ULIDMessages
# Reply to a ticket
there-there reply-to-ticket --ticket=ULID --field body="<p>Thanks for reaching out!</p>"
# Forward a ticket
there-there forward-ticket --ticket=ULID --field body="<p>FYI</p>" --field to_recipients='["someone@example.com"]'
# Add an internal note
there-there add-note-to-ticket --ticket=ULID --field body="<p>Internal note here</p>"Activities
# List activities for a ticket
there-there list-ticket-activities --ticket=ULIDTags, channels, members, contacts
# List workspace tags
there-there list-tags
# List workspace channels
there-there list-channels
# List workspace members
there-there list-members
# List contacts (with search/filter)
there-there list-contacts --filter-search=john
# View a specific contact
there-there show-contact --contact=ULIDPagination
All list commands support pagination:
there-there list-tickets --page=2 --per-page=10Common workflows
Ticket triage
List open tickets, review them, and take action. See references/workflows.md for the full triage workflow.
Quick version:
# 1. List open tickets sorted by most recent
there-there list-tickets --filter-status=open --sort=-updated_at
# 2. View a ticket to read messages
there-there show-ticket --ticket=ULID
# 3. Reply to the customer
there-there reply-to-ticket --ticket=ULID --field body="<p>Your reply here</p>"
# 4. Close resolved tickets
there-there update-ticket-status --ticket=ULID --field status=closedSearching tickets
The list-tickets command supports two types of search plus date and assignee filters:
- `--q` (semantic search): Uses AI embeddings to find tickets by meaning, not just keywords. Matches across subjects, summaries, and message content. Results are ordered by relevance. Requires AI to be enabled for the workspace (returns 422 if not). Can be combined with all other filters.
- `--filter-search` (full-text search): Keyword search across ticket subjects, messages, and contact info. Faster than semantic search, good for finding exact terms or email addresses.
- `--filter-assigned-user-ulid`: Filter by a specific user ULID (use
list-membersto find ULIDs). Different from--filter-assigned-to-mewhich uses the authenticated user. - `--filter-created-after`: Only return tickets created on or after this date. ISO 8601 format (e.g.
2026-01-01). - `--filter-created-before`: Only return tickets created on or before this date. ISO 8601 format (e.g.
2026-03-01).
All filters can be combined. When --q is used without an explicit --sort, results are ordered by relevance. When --q is used with --sort, the explicit sort takes precedence.
# Semantic search for tickets about a topic
there-there list-tickets --q="how do I get a refund"
# Full-text search for a keyword
there-there list-tickets --filter-search="invoice"
# Combine semantic search with filters
there-there list-tickets --q="billing issue" --filter-status=open --filter-created-after=2026-01-01
# Tickets from a specific user in a date range
there-there list-tickets --filter-assigned-user-ulid=USER_ULID --filter-created-after=2026-01-01 --filter-created-before=2026-02-01Bulk operations
# Find all unassigned tickets
there-there list-tickets --filter-unassigned=true
# Assign them
there-there update-ticket-assignee --ticket=ULID --field assignee_ulid=USER_ULIDOutput format
All commands return JSON. When presenting results to the user:
- Tickets: Show as a table with columns: subject, status, channel, assignee, tags, last message preview, updated at.
- Messages: Show sender, type (inbound/outbound/note), body preview, attachments count, inline-image count, timestamp.
- Inline images: Customer-pasted screenshots come back on each message under
inline_images[]. Theirurlanddownload_urlneed the same Bearer token as the API; see references/commands.md for acurlsnippet. - Contacts: Show name, email, ticket count, last activity.
- Activities: Show type, user, description, timestamp.
Command reference
Complete reference for all there-there CLI commands. Every command outputs JSON.
Global options
All commands accept these options:
| Option | Description |
|---|---|
--profile | Use a specific profile instead of the default |
Profiles
profiles
List all configured profiles. The active default profile is marked with *.
there-there profilesuse
Switch the default profile.
there-there use {profile}Required arguments:
| Argument | Description |
|---|---|
profile | Name of the profile to switch to |
login
Store an API token. Automatically creates a profile named after the workspace.
there-there login
there-there login --profile=spatieOptional parameters:
| Parameter | Description |
|---|---|
--profile | Profile name to store credentials under (defaults to slugified workspace name) |
logout
Remove stored credentials.
there-there logout
there-there logout --profile=spatie
there-there logout --allOptional parameters:
| Parameter | Description |
|---|---|
--profile | Remove a specific profile |
--all | Remove all profiles |
---
User & Workspace
get-me
Get the currently authenticated user and workspace.
there-there get-meParameters: None
Response fields: user (id, name, email, avatar_url, timezone), workspace (id, name, slug)
---
Tickets
list-tickets
List all tickets (paginated).
there-there list-ticketsOptional parameters:
| Parameter | Type | Description |
|---|---|---|
--filter-status | string | Filter by status (comma separated): open, closed, spam |
--filter-tag-ulids | string | Filter by tag ULIDs (comma separated) |
--filter-channel-ulids | string | Filter by channel ULIDs (comma separated) |
--filter-assigned-to-me | boolean | Only tickets assigned to the authenticated user |
--filter-unassigned | boolean | Only unassigned tickets |
--q | string | Semantic search query. Uses AI embeddings to find tickets by meaning. Results ordered by relevance. |
--filter-search | string | Full-text keyword search across subjects, messages, and contact info |
--filter-assigned-user-ulid | string | Filter by specific assignee user ULID |
--filter-created-after | string | Only tickets created on or after this date (ISO 8601, e.g. 2026-01-01) |
--filter-created-before | string | Only tickets created on or before this date (ISO 8601, e.g. 2026-03-01) |
--sort | string | Sort field. Prefix with - for descending. Allowed: created_at, updated_at, last_activity_at |
--per-page | integer | Items per page (max 100, default 25) |
--page | integer | Page number (default: 1) |
Response: Paginated. Each ticket has: id, ulid, subject, status, channel, assignee, contact, tags[], latest_message_preview, summary, created_at, updated_at
show-ticket
Get ticket details with messages and activities.
there-there show-ticket --ticket=ULIDRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Response: Full ticket with messages[] and activities[]. Each message has: id, ulid, type (inbound/outbound/note), body_html, sender, attachments[], inline_images[], is_forward, created_at. Each activity has: id, type, user, properties, description, created_at.
Inline images: inline_images[] exposes images embedded inside body_html (typically bug screenshots customers paste into emails). Each item has id, name, mime_type, size, content_id, url (matches the <img src> in the body), and download_url. Fetch the bytes with the same Bearer token the CLI uses (stored in ~/.there-there/config.json under the active profile):
TOKEN=$(jq -r '.profiles[.default_profile].token' ~/.there-there/config.json)
curl -L -H "Authorization: Bearer $TOKEN" "$URL" -o screenshot.pngupdate-ticket-status
Change a ticket's status.
there-there update-ticket-status --ticket=ULID --field status=closedRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Required body fields (via `--field`):
| Field | Type | Values |
|---|---|---|
status | string | open, closed, spam |
update-ticket-assignee
Assign or unassign a user from a ticket.
there-there update-ticket-assignee --ticket=ULID --field assignee_ulid=USER_ULIDRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Body fields (via `--field`):
| Field | Type | Description |
|---|---|---|
assignee_ulid | string/null | User ULID to assign, or null to unassign |
update-ticket-team
Assign or unassign a team from a ticket.
there-there update-ticket-team --ticket=ULID --field team_ulid=TEAM_ULIDRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Body fields (via `--field`):
| Field | Type | Description |
|---|---|---|
team_ulid | string/null | Team ULID to assign, or null to unassign |
add-tag-to-ticket
Add a tag to a ticket.
there-there add-tag-to-ticket --ticket=ULID --tag=TAG_ULIDRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
--tag | string | Tag ULID |
remove-tag-from-ticket
Remove a tag from a ticket.
there-there remove-tag-from-ticket --ticket=ULID --tag=TAG_ULIDRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
--tag | string | Tag ULID |
list-ticket-activities
List activities for a ticket (paginated).
there-there list-ticket-activities --ticket=ULIDRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Optional parameters:
| Parameter | Type | Description |
|---|---|---|
--page | integer | Page number (default: 1) |
---
Messages
reply-to-ticket
Send a reply to a ticket.
there-there reply-to-ticket --ticket=ULID --field body="<p>Your reply</p>"Required parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Required body fields:
| Field | Type | Description |
|---|---|---|
body | string | HTML body of the reply |
Optional body fields:
| Field | Type | Description |
|---|---|---|
to_recipients | array | Email addresses |
cc_recipients | array | CC email addresses |
bcc_recipients | array | BCC email addresses |
forward-ticket
Forward a ticket to external recipients.
there-there forward-ticket --ticket=ULID --field body="<p>FYI</p>" --field to_recipients='["someone@example.com"]'Required parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Required body fields:
| Field | Type | Description |
|---|---|---|
body | string | HTML body of the forward |
to_recipients | array | At least one email address |
Optional body fields:
| Field | Type | Description |
|---|---|---|
cc_recipients | array | CC email addresses |
bcc_recipients | array | BCC email addresses |
forwarded_from_message_ulid | string | ULID of the original message being forwarded |
add-note-to-ticket
Add an internal note to a ticket.
there-there add-note-to-ticket --ticket=ULID --field body="<p>Internal note</p>"Required parameters:
| Parameter | Type | Description |
|---|---|---|
--ticket | string | Ticket ULID |
Required body fields:
| Field | Type | Description |
|---|---|---|
body | string | HTML body of the note |
---
Tags
list-tags
List all workspace tags.
there-there list-tagsParameters: None
Response: Array of tags, each with: id, ulid, name, color
---
Channels
list-channels
List all workspace channels.
there-there list-channelsParameters: None
Response: Array of channels, each with: id, name, type, color
---
Members
list-members
List all workspace members.
there-there list-membersParameters: None
Response: Array of users, each with: id, name, email, avatar_url, timezone
---
Contacts
list-contacts
List contacts (paginated).
there-there list-contactsOptional parameters:
| Parameter | Type | Description |
|---|---|---|
--filter-name | string | Filter by name (partial match) |
--filter-email | string | Filter by email (partial match) |
--filter-search | string | Search by name or email |
--sort | string | Sort field. Prefix with - for descending. Allowed: name, created_at, last_activity |
--per-page | integer | Items per page (max 100, default 25) |
--page | integer | Page number (default: 1) |
Response: Paginated. Each contact has: id, ulid, name, email, avatar_url, ticket_count, last_activity_at, notes
show-contact
Get contact details.
there-there show-contact --contact=ULIDRequired parameters:
| Parameter | Type | Description |
|---|---|---|
--contact | string | Contact ULID |
Response: Contact object with: id, ulid, name, email, avatar_url, ticket_count, last_activity_at, notes
Workflows
Detailed workflows for common There There CLI tasks.
Working with multiple workspaces
If you manage tickets across multiple workspaces, set up a profile for each.
# Log in to each workspace
there-there login --profile=spatie
there-there login --profile=ohdear
# Check which profiles you have
there-there profiles
# Switch your default workspace
there-there use spatie
# Or run a one-off command against a specific workspace
there-there list-tickets --profile=ohdear---
Ticket triage
Systematically work through open tickets, review them, and take action.
Step 1: Get an overview
# List open tickets, most recently updated first
there-there list-tickets --filter-status=open --sort=-updated_at --per-page=30Present results as a table: subject, status, channel, assignee, tags, last message preview, updated at.
Step 2: Categorize tickets
Group tickets by:
- Channel (email, widget, API) to understand where requests come from
- Unassigned tickets that need someone to pick them up
- Tags to identify common topics or priorities
Step 3: Review each ticket
For each ticket that needs attention:
# View the full ticket with messages
there-there show-ticket --ticket=ULIDRead the messages to understand the customer's issue. Check the activities to see what actions have already been taken.
Step 4: Take action
| Situation | Action |
|---|---|
| Can answer immediately | there-there reply-to-ticket --ticket=ULID --field body="<p>Your reply</p>" |
| Needs someone else | there-there update-ticket-assignee --ticket=ULID --field assignee_ulid=USER_ULID |
| Issue is resolved | there-there update-ticket-status --ticket=ULID --field status=closed |
| Needs internal discussion | there-there add-note-to-ticket --ticket=ULID --field body="<p>Note for team</p>" |
| Should be tagged | there-there add-tag-to-ticket --ticket=ULID --tag=TAG_ULID |
| Is spam | there-there update-ticket-status --ticket=ULID --field status=spam |
Step 5: Paginate through remaining tickets
# Next page
there-there list-tickets --filter-status=open --sort=-updated_at --page=2 --per-page=30Repeat until all pages are triaged. Use meta.last_page from the response to know when you're done.
---
Search tickets
Semantic search (AI-powered)
Find tickets by meaning, not just keywords. Useful for finding tickets about a topic even when exact words differ.
# Find tickets about refunds
there-there list-tickets --q="how do I get a refund"
# Find billing-related open tickets
there-there list-tickets --q="billing problem" --filter-status=open
# Find recent tickets about a topic
there-there list-tickets --q="password reset" --filter-created-after=2026-01-01Semantic search requires AI to be enabled for the workspace. Results are ordered by relevance.
Full-text keyword search
Search for exact keywords across ticket subjects, messages, and contact info.
# Search by keyword
there-there list-tickets --filter-search="refund"
# Search by email
there-there list-tickets --filter-search="john@example.com" --filter-status=openFilter by date range
# Tickets from January 2026
there-there list-tickets --filter-created-after=2026-01-01 --filter-created-before=2026-02-01
# Recent tickets
there-there list-tickets --filter-created-after=2026-03-01 --filter-status=openFilter by specific assignee
# List workspace members to find user ULIDs
there-there list-members
# Filter by assignee
there-there list-tickets --filter-assigned-user-ulid=USER_ULID---
Respond to a ticket
Step 1: View the ticket
there-there show-ticket --ticket=ULIDRead all messages to understand the full conversation history.
Step 2: Reply
there-there reply-to-ticket --ticket=ULID --field body="<p>Your reply HTML here</p>"For replies with CC/BCC:
there-there reply-to-ticket --ticket=ULID \
--field body="<p>Reply</p>" \
--field to_recipients='["customer@example.com"]' \
--field cc_recipients='["manager@example.com"]'Step 3: Close if resolved
there-there update-ticket-status --ticket=ULID --field status=closed---
Forward a ticket
Forward a ticket to an external party for help or escalation.
# View the ticket first
there-there show-ticket --ticket=ULID
# Forward with context
there-there forward-ticket --ticket=ULID \
--field body="<p>Can you help with this customer issue?</p>" \
--field to_recipients='["expert@partner.com"]'---
Manage contacts
Find a contact
# Search by name or email
there-there list-contacts --filter-search=john
# View contact details
there-there show-contact --contact=ULIDView a contact's tickets
Currently, use the contact's email to search for their tickets or view the contact in the web UI for linked tickets.
---
Assign and organize
Assign tickets to team members
# List workspace members to find user ULIDs
there-there list-members
# Assign a ticket
there-there update-ticket-assignee --ticket=ULID --field assignee_ulid=USER_ULID
# Assign to a team
there-there update-ticket-team --ticket=ULID --field team_ulid=TEAM_ULIDTag tickets for organization
# List available tags
there-there list-tags
# Add a tag
there-there add-tag-to-ticket --ticket=ULID --tag=TAG_ULID
# Remove a tag
there-there remove-tag-from-ticket --ticket=ULID --tag=TAG_ULID---
Daily support routine
A suggested daily workflow:
# 1. Check unassigned tickets
there-there list-tickets --filter-unassigned=true --sort=-updated_at
# 2. Check your assigned tickets
there-there list-tickets --filter-assigned-to-me=true --filter-status=open
# 3. Review and respond to each
there-there show-ticket --ticket=ULID
there-there reply-to-ticket --ticket=ULID --field body="<p>Reply</p>"
# 4. Close resolved tickets
there-there update-ticket-status --ticket=ULID --field status=closed