# Settle match prediction

Send a match's final score to start paying rewards to the traders who predicted it correctly.

- Endpoint: `POST https://api.returning.ai/v1/match-predictions/{matchPredictionId}/settlements`
- Section: Gamification / Match predictions
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `matchPredictions` (Shown in the dashboard as "Match Prediction")
- Retries: Idempotency-Key required; same key and body replays
- Guide: hand-written
- Verified: code, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/gamification-match-predictions/settle-match-prediction

## When to use this

- The match has finished and your results feed has the final score.
- An earlier settlement failed and you need to run it again.

**Instead:** Use [Void match prediction](https://docs.returning.ai/api-reference/gamification-match-predictions/void-match-prediction.md) instead when the match was cancelled or abandoned, to refund entry costs without paying rewards.

**Availability:** api.returning.ai doesn't serve this route yet: requests return an empty `404`. Ask Returning.AI before you build on it.

**No undo:** A settled match can't be settled again or voided. Check the score before you send it.

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `matchPredictions`

Use a Community API key with `matchPredictions`, shown in the dashboard as Match Prediction, and keep it on your server. The key decides the community: you only see and change match predictions in that community, and an ID from another community returns `404`, the same as an unknown ID. Personal API keys are rejected with `403`.

## Behaviour

api.returning.ai doesn't serve this route yet: requests return an empty `404`. Ask Returning.AI before you build on it.

Settlement runs in the background: a `202` means it has started, not finished. Poll Get settlement results until it returns `200`. While it runs, the match shows `settlement_in_progress`.

You can settle once `kickoffAt` has passed, and only a published match that isn't already settled, voided or being voided. A match whose settlement failed can be settled again.

Each prediction is scored against the score you send:

- Right result (team A win, team B win or draw), wrong score: `rewards.correctResult`.
- Right result and exact score: `rewards.correctResult` plus `rewards.exactScoreBonus`.
- Wrong result: nothing.

Winners also get their entry cost back, added to their coin payout. Settlement also sends your community's match prediction webhooks, if you have set any up.

## Request

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `matchPredictionId` | `string` | Yes | The match prediction ID from Create match prediction or List match predictions. (`mp_` and 24 hex characters) |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `matchPredictions`. (`Bearer <API_KEY>`) |
| `Idempotency-Key` | `string` | Yes | A unique key you create for this settlement, such as a UUID. Reuse it only to retry the same body. (1-255 characters) |
| `Content-Type` | `string` | Yes | Request body format. Anything else returns `415`. (`application/json`) |
| `X-Request-Id` | `string` | No | Your own ID for this request, returned in `meta.requestId`. Without it, a `req_...` ID is generated. |

### Body

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fullTimeScore` | `object` | Yes | The final score to settle on. It is used as sent, whatever `scoreBasis` says. |
| `fullTimeScore.teamA` | `integer` | Yes | Team A's goals. (Whole number, 0 or more) |
| `fullTimeScore.teamB` | `integer` | Yes | Team B's goals. (Whole number, 0 or more) |

### Example request

```bash
curl --request POST \
  --url https://api.returning.ai/v1/match-predictions/mp_66f000000000000000000801/settlements \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Idempotency-Key: 3f9c2a7e-1b4d-4e8a-9c61-2d5f7a8b9e10' \
  --header 'Content-Type: application/json' \
  --data '{
    "fullTimeScore": {
      "teamA": 2,
      "teamB": 1
    }
  }'
```

## Response

A `202` returns the settlement's `operationId` and the `resultsUrl` to poll. If a settlement is already running on this match under a different key, you get `202 MATCH_PREDICTION_SETTLEMENT_IN_PROGRESS` with the running settlement's details instead; poll its results rather than starting another.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `meta` | `object` | always | Result details. |
| `meta.status` | `string` | always | Result of the request. (`success`) |
| `meta.statusCode` | `integer` | always | The HTTP status, repeated. |
| `meta.code` | `string` | always | Machine-readable result code. (`MATCH_PREDICTION_SETTLEMENT_ACCEPTED`, or `MATCH_PREDICTION_SETTLEMENT_IN_PROGRESS` when a settlement is already running) |
| `meta.requestId` | `string` | always | Your `X-Request-Id` if you sent one, otherwise a generated `req_...` ID. Quote it when you contact support. |
| `message` | `string` | always | Human-readable summary. Do not branch on it. |
| `data` | `object` | always | The settlement that was started. |
| `data.operationId` | `string` | always | The settlement ID. (`mps_` and 32 hex characters) |
| `data.type` | `string` | always | Always `settlement`. |
| `data.status` | `string` | always | `settlement_in_progress` when it starts. A replay shows the status the settlement had then. |
| `data.submittedAt` | `string` | always | When the settlement was accepted. |
| `data.resultsUrl` | `string` | always | Where to poll for the outcome, without the host. |

### Example response (202)

```json
{
  "meta": {
    "status": "success",
    "statusCode": 202,
    "code": "MATCH_PREDICTION_SETTLEMENT_ACCEPTED",
    "requestId": "req_66f00000000000000000000000000811"
  },
  "message": "Match prediction settlement accepted.",
  "data": {
    "operationId": "mps_66f00000000000000000000000000803",
    "type": "settlement",
    "status": "settlement_in_progress",
    "submittedAt": "2026-10-10T21:00:00.000Z",
    "resultsUrl": "/v1/match-predictions/mp_66f000000000000000000801/results"
  }
}
```

## Errors

Every error carries its code in `meta.code` and the request's ID in `meta.requestId`. Validation errors list each problem in `details`, with a `field`, `code` and `message`. The API key checks (`401`, `403`, `404 COMMUNITY_NOT_FOUND` and `500 AUTHENTICATION_FAILED`) return `detail` and `solution` instead. A `404` with an empty body means the route isn't available on api.returning.ai yet. A `400` or `409` changes nothing.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 404 | - | The body is empty: api.returning.ai doesn't serve this route yet. Ask Returning.AI before you build on it. |
| 400 | `MATCH_PREDICTION_VALIDATION_FAILED` | `fullTimeScore` is missing, a score isn't a whole number of 0 or more, or the body has another field. `details` names it. Nothing changed. |
| 400 | `READ_ONLY_FIELD` | The request has a field the server sets, such as `status` or `settlement`. `details` names it. Remove it. |
| 400 | `INVALID_JSON` | The body is not valid JSON. Fix the syntax and send it again. |
| 400 | `IDEMPOTENCY_KEY_REQUIRED` | Send an `Idempotency-Key` header with a unique value. |
| 400 | `IDEMPOTENCY_KEY_INVALID` | The `Idempotency-Key` is longer than 255 characters. Use a shorter key, such as a UUID. |
| 415 | `UNSUPPORTED_MEDIA_TYPE` | Send the body as JSON with `Content-Type: application/json`. |
| 401 | `AUTHENTICATION_REQUIRED` | The key is missing, invalid or expired. Send `Authorization: Bearer <API_KEY>` with a current Community API key. |
| 403 | `API_KEY_PERMISSION_DENIED` | The key lacks `matchPredictions`, or it is a personal key. Use a Community API key and add Match Prediction in Settings > Integration > API Keys. |
| 409 | `IDEMPOTENCY_KEY_CONFLICT` | This `Idempotency-Key` was already used with a different body. Use a new key for a new body. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `MATCH_PREDICTION_NOT_FOUND` | No match prediction in your community has this ID. Check it is the full `mp_...` ID from Create match prediction or List match predictions. |
| 404 | `COMMUNITY_NOT_FOUND` | The community this key belongs to no longer exists. Contact Returning.AI support. |
| 409 | `MATCH_NOT_STARTED` | `kickoffAt` hasn't passed yet. Settle after kickoff, or move `kickoffAt` with Patch match prediction if the match started early. |
| 409 | `INVALID_LIFECYCLE_TRANSITION` | The match is a draft, or already `settled` or `voided`. Only a published match that isn't finished can be settled. Nothing changed. |
| 409 | `OPERATION_IN_PROGRESS` | A void is running on this match, so it can't be settled. Check the outcome with Get settlement results. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 429 | `RATE_LIMITED` | Your community is over its [rate limit](https://docs.returning.ai/how-it-works.md#rate-limits). Wait for the seconds in `Retry-After`, then retry. |
| 500 | `INTERNAL_ERROR` | The outcome is unclear. Check Get settlement results before you send the settlement again with a new key. |
| 500 | `AUTHENTICATION_FAILED` | The key could not be checked. Retry with backoff; nothing changed. |
| 503 | `SETTLEMENT_QUEUE_UNAVAILABLE` | Settlement couldn't be started, and nothing changed. Retry later with a new `Idempotency-Key`: the same key returns this `503` again. |
| 503 | `MATCH_PREDICTION_FROZEN` | Returning.AI has paused match prediction requests. Nothing changed. Retry later with backoff. |

**Retries:** Every settlement needs an `Idempotency-Key`. The same key with the same body returns the first response again and never settles twice. The same key with a different score returns `409 IDEMPOTENCY_KEY_CONFLICT`. Responses are replayed for 24 hours, including errors, so after a `503` or a `500` use a new key. Before you send a new key after a timeout, check Get settlement results: a settlement that already started returns `202` there. Returning.AI can set a request limit for your community on these endpoints, counted per community and calling IP address. Over it, requests return `429 RATE_LIMITED` with a `Retry-After` header in seconds; wait that long, then retry.

## Next step

- [Get settlement results](https://docs.returning.ai/api-reference/gamification-match-predictions/get-settlement-results.md): `GET /v1/match-predictions/{matchPredictionId}/results`. Poll until the settlement finishes, then read the totals and each trader's payout.
- [Read the settlement totals with Get match prediction](https://docs.returning.ai/api-reference/gamification-match-predictions/get-match-prediction.md): `GET /v1/match-predictions/{matchPredictionId}`.
