> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyclemate.club/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit a report

> Send a road or infrastructure report and receive a report ID

## Overview

Accepts a single report and returns a report ID immediately. A report is a categorised observation about a road at a coordinate. The report enters the processing pipeline (AI analysis plus human review). Poll [`GET /reports/{id}`](/api-reference/reports/get-report) for the outcome, typically available within 15 minutes.

### Authentication

This endpoint requires an API key. Include it in the `Authorization` header:

```
Authorization: Bearer YOUR_API_KEY
```

A signed-in user session token is also accepted. Our apps submit rider reports this way. Anonymous callers are rejected: every report must be attributable to a submitter.

### Rate limits

User session tokens are limited to **60 reports per hour**. Exceeding the limit returns `429` with a `Retry-After` header and a `retry_after_seconds` field in the body. Partner API keys carry limits agreed at issuance. [Contact us](https://cyclemate.club/for-developers) about expected volumes.

## Request body

<ParamField body="issue_type" type="string" required>
  The report category. One of:

  | Value                  | Meaning                                                                           |
  | ---------------------- | --------------------------------------------------------------------------------- |
  | `road_closed`          | The road is impassable: closure, street works, blockage.                          |
  | `road_unsafe`          | A safety hazard: dangerous junction, surface hazard, conflict with motor traffic. |
  | `bad_routing`          | We routed through here when a better route exists.                                |
  | `unclear_instructions` | A navigation instruction at this location was confusing or wrong.                 |
  | `unpleasant`           | Rideable but unpleasant: rough surface, crowding, steep gradient.                 |
  | `other`                | Anything else. Describe it in `description`.                                      |
</ParamField>

<ParamField body="location" type="[number, number]" required>
  The report coordinate as `[longitude, latitude]` (GeoJSON order, see [Coordinate format](/api-reference/introduction#coordinate-format)).
</ParamField>

<Accordion title="Optional parameters">
  <ParamField body="description" type="string">
    Free-text description of the issue, up to 2,000 characters. The more specific, the faster the analysis. Include what is blocked, since when, and until when if known.
  </ParamField>

  <ParamField body="road_name" type="string">
    Name of the affected road or place, up to 200 characters.
  </ParamField>

  <ParamField body="issue_subtype" type="string">
    The sub-category, which says what kind of problem it is within the `issue_type`. Sending one classifies the report without waiting for a human to read the description, so it is the single most useful optional field. Each category recognises its own values:

    | `issue_type`           | Recognised `issue_subtype`                                                                                               |
    | ---------------------- | ------------------------------------------------------------------------------------------------------------------------ |
    | `road_closed`          | `private_locked`, `barrier_gate`, `no_cycling_zone`, `temporary_closure`, `permanent_closure`, `one_way`, `road_missing` |
    | `road_unsafe`          | `poor_bike_lane`, `too_many_peds`, `too_many_vehicles`                                                                   |
    | `bad_routing`          | `faster_route`, `quieter_route`, `fewer_turns`                                                                           |
    | `unclear_instructions` | `wrong_direction`, `unclear_instruction`, `missing_instruction`                                                          |
    | `unpleasant`           | `cobbles_rough`, `too_hilly`, `too_many_peds`                                                                            |

    `other` is accepted under every category. Under `road_closed`, the first five values mean the road is physically blocked and the last two mean the manoeuvre is illegal rather than the road impassable, which we act on differently. Any other string up to 64 characters is stored but is not classified.
  </ParamField>

  <ParamField body="source" type="string">
    **Required for partner API keys**, ignored for user session tokens. The source identifier agreed when your key was issued (e.g. `dft`, `forest`): a 2–32 character slug of `a-z`, `0-9`, `_`, `-`. It attributes the submission and scopes which reports your key can read back.
  </ParamField>
</Accordion>

## Response body

<ResponseField name="id" type="number">
  The report ID. Use it with [`GET /reports/{id}`](/api-reference/reports/get-report) to poll processing status.
</ResponseField>

<ResponseField name="status" type="string">
  Always `received` on creation.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp of receipt.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST 'https://api.cyclemate.club/reports/' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "issue_type": "road_closed",
      "location": [-0.0766, 51.5323],
      "description": "Carriageway closed for gas works, both directions",
      "road_name": "Kingsland Road",
      "source": "dft"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": 1287,
    "status": "received",
    "created_at": "2026-08-05T10:12:31.201Z"
  }
  ```
</ResponseExample>

## Variants

### Partner submission

A partner API key must send its agreed `source` slug on every report. Submissions are attributed to that slug, and the key can read back any report submitted through the API.

### User session submission

A signed-in user token submits without `source` (any value sent is ignored). The report is attributed to the user's account, and the token can read back only that user's reports.

## Error responses

### 400 Bad request

Invalid body: an unknown `issue_type`, an out-of-range `location`, or a missing `source` on a partner key. The `error` field names the problem.

```json theme={null}
{
  "error": "issue_type must be one of: road_unsafe, road_closed, bad_routing, unclear_instructions, unpleasant, other"
}
```

### 401 Unauthorized

Missing or invalid credentials, or an anonymous caller.

```json theme={null}
{
  "error": "Endpoint requires one of: user, service"
}
```

### 405 Method not allowed

Method other than `POST` on the collection.

```json theme={null}
{
  "error": "Method not allowed"
}
```

### 429 Too many requests

Rate limit exceeded, see [Rate limits](#rate-limits). Includes a `Retry-After` header.

```json theme={null}
{
  "error": "Rate limit exceeded",
  "retry_after_seconds": 45
}
```

### 500 Internal server error

Unexpected server failure.

```json theme={null}
{
  "error": "Internal server error"
}
```
