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

# Record Decision

> Report a provider's approval, rejection, or referral decision back to Klara

Records a decision on a submitted application. Use this endpoint when a downstream provider (e.g. a bank, payment provider, or credit bureau) returns an outcome for an application and you want Klara to have a canonical record of that decision.

```text theme={null}
POST /api/external/applications/{id}/decision
```

## Authentication

Requires an API key with `applications:write` scope. The application must belong to the organization associated with the API key.

```bash theme={null}
Authorization: Bearer klara_{client_id}.{secret}
```

## Path parameters

<ParamField path="id" type="string" required>
  UUID of the application. Must match the `applicationId` in the request body.
</ParamField>

## Request body

<ParamField body="type" type="string" required>
  Decision type. One of:

  * `application.approved`
  * `application.rejected`
  * `application.referred`
</ParamField>

<ParamField body="timestamp" type="string" required>
  ISO 8601 timestamp of when the provider made the decision (not when you are relaying it to Klara).
</ParamField>

<ParamField body="application_id" type="string" required>
  UUID of the application. Must match the `{id}` in the URL path.
</ParamField>

<ParamField body="data" type="object">
  Free-form object containing any provider-specific context you want stored alongside the decision — for example a provider reference number, reason codes, or reviewer notes. Stored verbatim.
</ParamField>

<Note>
  Provider decisions can only be recorded against applications that have been submitted. Calling this endpoint for an application still in `created`, `sent`, or `in_progress` state returns `400`.
</Note>

### Idempotency

Recording the **same decision type** twice for an application is a safe no-op. The second call returns `200 OK` with `already_recorded: true` and does not create a duplicate record.

Different decision types are all recorded in order — e.g. a `referred` decision followed later by an `approved` decision will both be stored.

## Response

Returns `201 Created` when a new decision is recorded, or `200 OK` if the decision was already recorded.

<ResponseField name="success" type="boolean">
  `true` when the decision was accepted.
</ResponseField>

<ResponseField name="decision_type" type="string">
  Normalized decision type (`approved`, `rejected`, or `referred`) — the `application.` prefix is stripped. Only present on `201`.
</ResponseField>

<ResponseField name="application_id" type="string">
  UUID of the application. Only present on `201`.
</ResponseField>

<ResponseField name="already_recorded" type="boolean">
  `true` when the same decision type was already recorded. Only present on `200`.
</ResponseField>

<ResponseField name="message" type="string">
  Human-readable explanation. Only present on `200`.
</ResponseField>

## Examples

### Approving an application

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.klara-ai.com/api/external/applications/550e8400-e29b-41d4-a716-446655440000/decision \
    -H "Authorization: Bearer klara_abc123def456.your-secret-here" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "application.approved",
      "timestamp": "2026-04-20T14:32:00Z",
      "application_id": "550e8400-e29b-41d4-a716-446655440000",
      "data": {
        "providerRef": "MOD-1234",
        "notes": "Clear"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const applicationId = '550e8400-e29b-41d4-a716-446655440000';

  const response = await fetch(
    `https://app.klara-ai.com/api/external/applications/${applicationId}/decision`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer klara_abc123def456.your-secret-here',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        type: 'application.approved',
        timestamp: new Date().toISOString(),
        application_id: applicationId,
        data: {
          providerRef: 'MOD-1234',
          notes: 'Clear'
        }
      })
    }
  );

  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests
  from datetime import datetime, timezone

  application_id = '550e8400-e29b-41d4-a716-446655440000'

  response = requests.post(
      f'https://app.klara-ai.com/api/external/applications/{application_id}/decision',
      headers={
          'Authorization': 'Bearer klara_abc123def456.your-secret-here',
          'Content-Type': 'application/json'
      },
      json={
          'type': 'application.approved',
          'timestamp': datetime.now(timezone.utc).isoformat(),
          'application_id': application_id,
          'data': {
              'providerRef': 'MOD-1234',
              'notes': 'Clear'
          }
      }
  )

  data = response.json()
  ```
</CodeGroup>

**Response (new decision):**

```json theme={null}
{
  "success": true,
  "decision_type": "approved",
  "application_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

**Response (already recorded):**

```json theme={null}
{
  "success": true,
  "message": "Provider decision 'approved' was already recorded for this application",
  "already_recorded": true
}
```

### Referring for manual review

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.klara-ai.com/api/external/applications/550e8400-e29b-41d4-a716-446655440000/decision \
    -H "Authorization: Bearer klara_abc123def456.your-secret-here" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "application.referred",
      "timestamp": "2026-04-20T14:32:00Z",
      "application_id": "550e8400-e29b-41d4-a716-446655440000",
      "data": {
        "reasonCodes": ["PEP_MATCH", "HIGH_RISK_JURISDICTION"],
        "reviewerQueue": "enhanced-dd"
      }
    }'
  ```
</CodeGroup>

## Errors

<ResponseExample>
  ```json 400 Bad Request - Invalid body theme={null}
  {
    "error": "Invalid request body",
    "details": {
      "type": ["Invalid enum value. Expected 'application.approved' | 'application.rejected' | 'application.referred'"]
    }
  }
  ```

  ```json 400 Bad Request - ID mismatch theme={null}
  {
    "error": "application_id in body does not match URL parameter"
  }
  ```

  ```json 400 Bad Request - Not submitted theme={null}
  {
    "error": "Application has not been submitted yet"
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": "Invalid API key"
  }
  ```

  ```json 404 Not Found theme={null}
  {
    "error": "Application not found"
  }
  ```
</ResponseExample>

| Error                                                 | Cause                                                                           |
| ----------------------------------------------------- | ------------------------------------------------------------------------------- |
| `Invalid JSON body`                                   | Request body is not valid JSON                                                  |
| `Invalid request body`                                | Body failed schema validation — see `details` for per-field errors              |
| `application_id in body does not match URL parameter` | The `application_id` field does not match the `{id}` in the URL                 |
| `Application has not been submitted yet`              | The application is still in a writable state (`created`, `sent`, `in_progress`) |
| `Invalid API key`                                     | API key is invalid, expired, or inactive                                        |
| `API key missing required scopes`                     | API key does not have the `applications:write` scope                            |
| `Application not found`                               | The application does not exist or belongs to a different organization           |
