# Redmine

## What it does

The Redmine integration lets Speedwave read and update issues, time entries, journals, and users in your Redmine instance. It supports self-hosted and on-premise instances, including private addresses and Tailscale CGNAT ranges. Setting a project during setup scopes Speedwave's work to it, so it fits naturally into [planning and turning requirements into user stories](/docs/features/planning-and-user-stories/).

## What it consists of

Speedwave runs Redmine as a worker container and routes Claude's tool calls to it. The worker mounts a read-only file at `/tokens/api_key` holding the API key, and reads a `config.json` in the same folder for the Redmine URL, an optional project ID and name, and the ID mappings for status, priority, tracker, and activity. Both files live under `~/.speedwave/tokens/<project>/redmine/`.

- ~/.speedwave/tokens/&lt;project&gt;/redmine/
  - api_key
  - config.json

## How it works

You connect Redmine with an API key, not OAuth or a username and password. The Desktop app's configuration wizard validates the key, then fetches your projects, statuses, trackers, priorities, and activities so you can confirm the ID mappings. It auto-matches names by dropping each key's category prefix and comparing the rest to Redmine's names case-insensitively; a non-English instance won't auto-match, so those need picking by hand.

Mappings link friendly keys such as `status_new` or `priority_high` to a project's numeric Redmine IDs, since the same name can point to a different ID on each instance. You can add custom keys too; the config file accepts any `category_name` pair. Speedwave reads mappings with `getMappings` before creating or updating an issue, and `getConfig` returns the active project ID, name, and URL. An unmapped friendly name fails with an error listing the available values for that field.

Issue descriptions, notes, journals, and comments are Textile markup, not Markdown, and Speedwave sanitizes them before sending, stripping dangerous HTML tags and script-like URI schemes while keeping a safe set of formatting tags.

Setting `project_id` scopes more than the tool list: reads are forced to that project, mutations are checked against it first, and moving an issue elsewhere with `updateIssue` is blocked with a scope error. Missing `host_url` in `config.json` falls back to a `REDMINE_URL` environment variable.

API errors return plain guidance: `401` for a bad key, `403` for insufficient permission, `404` for a missing item, `422` for Redmine's own field validation errors, and a network-error message when the request never lands. Failed requests retry automatically up to 3 times, waiting 2, 4, then 8 seconds.

### ID mapping keys

| Category | Keys |
|---|---|
| Status | `status_new`, `status_in_progress`, `status_resolved`, `status_feedback`, `status_closed`, `status_rejected` |
| Priority | `priority_low`, `priority_normal`, `priority_high`, `priority_urgent`, `priority_immediate` |
| Tracker | `tracker_bug`, `tracker_feature`, `tracker_task`, `tracker_support` |
| Activity | `activity_design`, `activity_development`, `activity_testing`, `activity_documentation`, `activity_support`, `activity_management`, `activity_devops`, `activity_review` |

### What Speedwave can do

23 tools across seven areas. Deletes are permanent and cannot be undone.

| Tool | What it does |
|---|---|
| `listIssueIds` | Lists issue IDs with filters for project, status, assignee, tracker, and priority. |
| `getIssueFull` | Gets complete issue data, including custom fields, journals, attachments, and relations. |
| `searchIssueIds` | Searches issues by text query, optionally scoped to a project. |
| `createIssue` | Creates a new issue, with tracker, status, priority, assignee, and parent by ID or name. |
| `updateIssue` | Updates an existing issue's fields and adds a note in the same call. |
| `commentIssue` | Adds a comment (journal entry) to an issue. |
| `listJournals` | Lists journal entries (comments and history) for an issue. |
| `updateJournal` | Edits the notes on an existing journal entry. |
| `deleteJournal` (destructive) | Permanently removes a journal entry. |
| `listTimeEntries` | Lists logged time with filters for issue, project, user, and date range. |
| `createTimeEntry` | Logs time against an issue or project. |
| `updateTimeEntry` | Updates hours, activity, or comments on an existing time entry. |
| `listUsers` | Lists users, optionally filtered to a project's members. |
| `resolveUser` | Resolves `me`, a user ID, or a username to a user ID. |
| `getCurrentUser` | Returns the authenticated user's profile. |
| `listProjectIds` | Lists project IDs with a status filter. |
| `getProjectFull` | Gets complete project data, including trackers, categories, and modules. |
| `searchProjectIds` | Searches projects by name, identifier, or description. |
| `listRelations` | Lists an issue's relations to other issues. |
| `createRelation` | Links two issues with a relation type and, for `precedes` or `follows`, a delay in days. |
| `deleteRelation` (destructive) | Permanently removes a relation. |
| `getMappings` | Returns the project's ID mappings for status, priority, tracker, and activity. |
| `getConfig` | Returns the active project ID, name, and URL. |

Relation types: `relates`, `duplicates`, `duplicated`, `blocks`, `blocked`, `precedes`, `follows`, `copied_to`, `copied_from`.

## Set it up

1. In Redmine, open **My account** and copy the **API access key**.
2. Open the Redmine integration card in the Desktop app and start the wizard. Enter your **Redmine URL** and **API Key**, then click **Validate**.
3. Once validated, pick a project (or **All projects**) and confirm the ID mappings for status, priority, tracker, and activity.
4. Save. Restart the project's containers if prompted so the worker picks up the configuration.

<DesktopFrame screen="integrations" />
**More than 100 projects:** The wizard fetches up to 100 projects. If yours isn't listed, open it in the Redmine web UI, note its slug, and set `project_id` to that slug directly in `config.json`.

## Security boundaries

The Redmine worker mounts only its own API key, read-only, with no access to other services' tokens or credentials. See [how credentials are handled](/docs/security/credentials/) for how Speedwave isolates each worker's secrets. When `project_id` is unset, Speedwave can pass one per call instead of being locked to one project.
**Corporate CA or HTTP proxy:** The Desktop app uses bundled CA roots, not your OS certificate store, and does not detect system proxies. A custom corporate CA can cause a TLS error during validation, and an HTTP proxy can cause the connection to time out.