---
title: todo.sr.ht API reference
---
The todo.sr.ht API allows you to browse, create, and manage repositories on
todo.sr.ht programmatically. This API follows the [standard sourcehut API
conventions](../api-conventions.md).
## Authentication
Authentication is done via the [meta.sr.ht OAuth
flow](https://man.sr.ht/meta.sr.ht/oauth-api.md). The following OAuth scopes are
available for todo.sr.ht:
- **events:read**: read ticket events
- **trackers:read**, **trackers:write**: read & write tracker details
- **tickets:read**, **tickets:write**: read & write tickets & comments
## Resources
### Comment resource
```json
{
"id": integer,
"created": "timestamp",
"submitter": { short-form user resource },
"text": "string (markdown)",
"ticket" { short-form ticket resoruce }
}
```
Short form:
```json
{
"id": integer,
"created": "timestamp",
"submitter": { short-form user resource },
"text": "string (markdown)"
}
```
### Event resource
```json
{
"id": integer,
"created": "timestamp",
"event_type": [ ...list of event types... ],
"old_status": status or null,
"old_resolution": resolution or null,
"new_status": status or null,
"new_resolution": resolution or null,
"user": { short-form user resource } or null,
"ticket": { short-form ticket resource } or null,
"comment": { short-form comment resource } or null,
"label": [ ...list of strings... ] or null,
"by_user": { short-form user resource } or null,
"from_ticket": { short-form ticket resource } or null
}
```
**Event type** may be one or more of `created`, `comment`, `status_change`,
`label_added`, `label_removed`, `assigned_user`, `unassigned_user`,
`user_mentioned`, or `ticket_mentioned`.
**Status** fields may be one of `reported`, `confirmed`, `in_progress`,
`pending`, or `resolved`.
**Resolution** fields may be one of `unresolved`, `fixed`, `implemented`,
`wont_fix`, `by_design`, `invalid`, `duplicate`, or `not_our_bug`.
The `by_user` and `from_ticket` features are used for events where a ticket or
user was mentioned in a comment somewhere. The ticket field and user field are
set to the mentioned ticket or user, and the `by_user` or `from_ticket` field
are set according to the discussion where the mention occured.
### Label resource
```json
{
"name": "string",
"created": "timestamp",
"colors": {
"background": "#rrggbb",
"text": "#rrggbb"
},
"tracker": { short-form tracker resource }
}
```
Short form:
```json
{
"name": "string",
"colors": {
"background": "#rrggbb",
"text": "#rrggbb"
}
}
```
### Ticket resource
```json
{
"id": integer,
"ref": "string",
"tracker": { short-form tracker resource },
"title": "ticket subject",
"created": "timestamp",
"updated": "timestamp",
"submitter": { short-form user resource },
"description": "string (markdown)",
"status": status,
"resolution": resolution,
"permissions": {
"anonymous": [ ...list of strings... ] or null,
"submitter": [ ...list of strings... ] or null,
"user": [ ...list of strings... ] or null,
},
"labels": [ ...list of label names... ],
"assignees": [ ...list of short-form user resources... ]
}
```
**Status** fields may be one of `reported`, `confirmed`, `in_progress`,
`pending`, or `resolved`.
**Resolution** fields may be one of `unresolved`, `fixed`, `implemented`,
`wont_fix`, `by_design`, `invalid`, `duplicate`, or `not_our_bug`.
**Permissions** may be one or more of `browse`, `submit`, `comment`, `edit`, and
`triage`. If null, the permissions are inherited from the tracker.
Short form:
```json
{
"id": integer,
"ref": "string",
"tracker": { short-form tracker resource }
}
```
### Tracker resource
```json
{
"owner": { short-form user resource },
"created": "timestamp",
"updated": "timestamp",
"name": "string",
"description": "string (markdown)",
"default_permissions": {
"anonymous": [ ...list of permissions... ],
"submitter": [ ...list of permissions... ],
"user": [ ...list of permissions... ]
}
}
```
**Permissions** may be one or more of `browse`, `submit`, `comment`, `edit`, and
`triage`.
Short form:
```json
{
"owner": { short-form user resource },
"created": "timestamp",
"updated": "timestamp",
"name": "string",
}
```
## Endpoints
**Note**: usernames are prefixed with `~`.
### GET /api/user/:username
Retrieves a user resource.
### GET /api/user
Equivalent to [/api/user/:username](#GET-apiuserusername), implies the
authenticated user.
### GET /api/user/:username/trackers
List of [tracker resources](#tracker-resource) owned by this user.
**OAuth scope**: `trackers:read`
### GET /api/trackers
Equivalent to [/api/:username/trackers](#GET-apiusernametrackers), implies the
authenticated user.
**OAuth scope**: `trackers:read`
### POST /api/trackers
Creates a new [tracker resource](#tracker-resource).
**OAuth scope**: `trackers:write`
**Request body**
```json
{
"name": "tracker name",
"description": "tracker description (markdown)" (optional)
}
```
**Response**
The new [tracker resource](#tracker-resource)
### GET /api/user/:username/trackers/:tracker-name
Retrieves a [tracker resource](#tracker-resource).
**OAuth scope**: `trackers:read`
### GET /api/trackers/:tracker-name
Equivalent to
[/api/:username/trackers/:tracker-name](#GET-apiusernametrackerstrackername),
implies the authenticated user.
**OAuth scope**: `trackers:read`
### PUT /api/user/:username/trackers/:tracker-name
Updates a [tracker resource](#tracker-resource).
```json
{
"description": "tracker description (markdown)" (optional)
}
```
**OAuth scope**: `trackers:write`
### PUT /api/trackers/:tracker-name
Equivalent to
[/api/:username/trackers/:tracker-name](#PUT-apiusernametrackerstrackername),
implies the authenticated user.
**OAuth scope**: `trackers:write`
### DELETE /api/trackers/:tracker-name
Deletes a [tracker resource](#tracker-resource).
**OAuth scope**: `trackers:write`
### DELETE /api/trackers/:tracker-name
Equivalent to
[/api/:username/trackers/:tracker-name](#DELETE-apiusernametrackerstrackername),
implies the authenticated user.
**OAuth scope**: `trackers:write`
### GET /api/user/:username/trackers/:tracker-name/labels
List of [label resources](#label-resource) associated with this tracker.
**OAuth scope**: `trackers:read`
### GET /api/trackers/:tracker-name/labels
Equivalent to
[/api/:username/trackers/:tracker-name/labels](#GET-apiusernametrackerstrackernamelabels),
implies the authenticated user.
**OAuth scope**: `trackers:read`
### GET /api/user/:username/trackers/:tracker-name/tickets
List of [ticket resources](#ticket-resource) associated with this tracker.
**OAuth scope**: `tickets:read`
### GET /api/trackers/:tracker-name/tickets
Equivalent to
[/api/:username/trackers/:tracker-name/tickets](#GET-apiusernametrackerstrackernametickets),
implies the authenticated user.
**OAuth scope**: `tickets:read`
### POST /api/user/:username/trackers/:tracker-name/tickets
Creates a new [ticket resources](#ticket-resource) associated with this tracker.
**OAuth scope**: `tickets:write`
**Request body**
```json
{
"title": "ticket title",
"description": "ticket description (markdown)" (optional)
}
```
**Response**
The new [ticket resource](#ticket-resource)
### POST /api/trackers/:tracker-name/tickets
Equivalent to
[/api/:username/trackers/:tracker-name/tickets](#POST-apiusernametrackerstrackernametickets),
implies the authenticated user.
**OAuth scope**: `tickets:write`
### GET /api/user/:username/trackers/:tracker-name/tickets/:ticket-id
Retrieves a [ticket resource](#ticket-resource).
**OAuth scope**: `tickets:read`
### GET /api/trackers/:tracker-name/tickets/:ticket-id
Equivalent to
[/api/:username/trackers/:tracker-name/tickets/:ticket-id](#GET-apiusernametrackerstrackernameticketsticketid),
implies the authenticated user.
**OAuth scope**: `tickets:read`
### PUT /api/user/:username/trackers/:tracker-name/tickets/:ticket-id
Updates a [ticket resource](#ticket-resource). This endpoint is used to:
- Open or close tickets
- Comment on tickets
- Add or remove labels
**OAuth scope**: `tickets:write`
**Request body**
```json
{
"comment": "comment text (markdown)" (optional),
"status": "ticket status enum" (optional),
"resolution": "ticket resolution enum" (optional),
"labels": ["list of desired label names"] (optional)
}
```
**Ticket status enum**
One of:
- `reported`
- `confirmed`
- `in_progress`
- `pending`
- `resolved`
**Ticket resolution enum**
One of:
- `unresolved`
- `fixed`
- `implemented`
- `wont_fix`
- `by_design`
- `invalid`
- `duplicate`
- `not_our_bug`
**Response**
```json
{
"ticket": { affected ticket resource },
"events": [
list of event resources created by this update
]
}
```
### PUT /api/trackers/:tracker-name/tickets/:ticket-id
Equivalent to
[/api/:username/trackers/:tracker-name/tickets/:ticket-id](#PUT-apiusernametrackerstrackernameticketsticketid),
implies the authenticated user.
**OAuth scope**: `tickets:write`
### GET /api/user/:username/trackers/:tracker-name/tickets/:ticket-id/events
List of [event resources](#event-resource) associated with this ticket.
**OAuth scope**: `tickets:read`
### GET /api/trackers/:tracker-name/tickets/:ticket-id/events
Equivalent to
[/api/:username/trackers/:tracker-name/tickets/:ticket-id/events](#GET-apiusernametrackerstrackernameticketsticketidevents),
implies the authenticated user.
**OAuth scope**: `tickets:read`
## Webhooks
### /api/user/...
Webhook for user events. Includes the [standard webhook
endpoints](../api-conventions.md#webhooks)
#### tracker:create
Issued when the user creates a new tracker.
**OAuth scope**: `trackers:read`
**Request body**
The new [tracker resource](#tracker-resource).
#### tracker:update
Issued when the user updates a tracker.
**OAuth scope**: `trackers:read`
**Request body**
The affected [tracker resource](#tracker-resource).
#### tracker:delete
Issued when the user deletes a tracker.
**OAuth scope**: `trackers:read`
**Request body**
```json
{
"id": the affected tracker ID
}
```
#### ticket:create
Issued when the user creates a new ticket.
**OAuth scope**: `tickets:read`
**Request body**
The new [ticket resource](#ticket-resource).
#### event:create
Issued when a new event is created for a ticket on this tracker.
**OAuth scope**: `tickets:read`
**Request body**
The new [event resource](#event-resource).
### /api/user/:username/trackers/:tracker-name
Webhook for tracker events. Includes the [standard webhook
endpoints](../api-conventions.md#webhooks)
#### label:create
Issued when a new label is added to this tracker.
**OAuth scope**: `trackers:read`
**Request body**
The new [label resource](#label-resource).
#### label:delete
Issued when a label is removed from this tracker.
**OAuth scope**: `trackers:read`
**Request body**
```json
{
"id": affected label ID
}
```
#### ticket:create
Issued when a ticket is created for this tracker.
**OAuth scope**: `tickets:read`
**Request body**
The new [ticket resource](#ticket-resource).
### /api/user/:username/trackers/:tracker-name/tickets/:ticket-id
Webhook for ticket events. Includes the [standard webhook
endpoints](../api-conventions.md#webhooks)
#### ticket:update
Issued when this tickets details are updated.
**OAuth scope**: `tickets:read`
**Request body**
The updated [ticket resource](#ticket-resource).
#### event:create
Issued when a new event is created for this this ticket.
**OAuth scope**: `tickets:read`
**Request body**
The new [event resource](#event-resource).