# Event Fund Claims

**Author**: Josh S. Sakweli, Backend Lead — QBIT SPARK CO LIMITED  
**Version**: v1.0  
**Base URL**: `/api/v1/e-events/claims`

---

## Overview

When an event organizer sells tickets, revenue is held in escrow — one escrow entry per buyer checkout session. A **Fund Claim** is a formal request to release **all currently `HELD` escrows** for an event into the organizer's wallet. Every claim goes through a `PENDING → APPROVED / REJECTED` lifecycle, and funds only move on explicit admin approval.

Claim approval is an all-or-nothing escrow operation — it releases every `HELD` escrow for that event at the moment of approval. Partial escrow release is not supported by design (see [Financial Safety](#financial-safety-refund--claim-concurrency)). What the organizer does with the wallet balance after release is handled by the separate `DisbursementService`.

**Authentication**: All endpoints require `Authorization: Bearer <jwt_token>`

---

## Business Rules

| Rule | Detail |
|------|--------|
| One pending per event | At most ONE `PENDING` claim per event at any time |
| Claimable amount formula | `totalRevenue − totalRefunded − totalClaimed − totalPendingClaims` |
| Admin-only for active events | If event has not ended, only ADMIN can initiate a claim |
| Refund deadline | 3 days before event start. Past this → no refunds, organizer may claim early |
| Escrow release | Approval releases ALL `HELD` escrows for the event atomically → organizer wallet. No partial release |
| No partial amounts | Escrows are atomic per buyer. "80% release" has no business meaning at the escrow layer — staging happens via `DisbursementService` after funds land in wallet |
| Admin claims require note | `adminNote` is mandatory on admin-initiated claims |
| Refund safety | Refunds and approvals use pessimistic write lock on EscrowAccount to prevent race conditions |

---

## Claim Status Lifecycle

| Status | Meaning |
|--------|---------|
| `PENDING` | Submitted, awaiting admin review. Escrow NOT yet released |
| `APPROVED` | Admin approved. Escrow atomically released to organizer wallet |
| `REJECTED` | Admin rejected. No funds moved. Organizer may submit a new claim |
| `CANCELLED` | Organizer cancelled before admin action. No funds moved |

---

## Standard Response Format

### Success
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Fund claim submitted successfully",
  "action_time": "2026-04-27T10:30:45",
  "data": { }
}
```

### Error
```json
{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "A pending claim already exists for this event",
  "action_time": "2026-04-27T10:30:45",
  "data": "A pending claim already exists for this event"
}
```

---

## Endpoints

---

### 1. List All Claims (Admin)

**Endpoint**: `GET /api/v1/e-events/claims`  
**Access**: 🔒 `ROLE_SUPER_ADMIN` or `ROLE_STAFF_ADMIN`  
**Purpose**: Returns all fund claims across all events. Optionally filter by status.

**Query Parameters**:

| Parameter | Type | Required | Description | Default |
|-----------|------|----------|-------------|---------|
| `status` | `ClaimStatus` enum | No | Filter by: `PENDING`, `APPROVED`, `REJECTED`, `CANCELLED` | — (all returned) |

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Fund claims retrieved",
  "data": [
    {
      "claimId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "claimNumber": "EFC-2026-000001",
      "eventId": "uuid",
      "eventTitle": "Dar Jazz Night",
      "eventStatus": "PUBLISHED",
      "organizerId": "uuid",
      "organizerName": "Amina Hassan",
      "status": "PENDING",
      "claimedAmount": 50000.00,
      "totalRevenueSnapshot": 80000.00,
      "totalRefundedSnapshot": 5000.00,
      "totalPreviouslyClaimedSnapshot": 0.00,
      "totalPendingAtSubmission": 0.00,
      "currency": "TZS",
      "adminInitiated": false,
      "adminId": null,
      "adminNote": null,
      "organizerNote": "Requesting first partial claim",
      "reviewedById": null,
      "reviewerName": null,
      "reviewNote": null,
      "reviewedAt": null,
      "escrowsReleasedCount": 0,
      "escrowsSkippedCount": 0,
      "actualReleasedAmount": null,
      "initiatedAt": "2026-04-20T08:00:00",
      "updatedAt": "2026-04-20T08:00:00"
    }
  ]
}
```

**Response Fields**:

| Field | Type | Description |
|-------|------|-------------|
| `claimId` | `UUID` | Unique claim identifier |
| `claimNumber` | `string` | Human-readable reference, e.g. `EFC-2026-000001` |
| `eventId` | `UUID` | The event this claim belongs to |
| `eventTitle` | `string` | Display name of the event |
| `eventStatus` | `EventStatus` | Current event status |
| `organizerId` | `UUID` | Organizer's account ID |
| `organizerName` | `string` | Organizer's display name |
| `status` | `ClaimStatus` | Current claim status |
| `claimedAmount` | `BigDecimal` | Amount the organizer requested |
| `totalRevenueSnapshot` | `BigDecimal` | Organizer's net revenue at time of submission |
| `totalRefundedSnapshot` | `BigDecimal` | Total refunds issued at time of submission |
| `totalPreviouslyClaimedSnapshot` | `BigDecimal` | Already released to wallet before this claim |
| `totalPendingAtSubmission` | `BigDecimal` | Other pending claim amounts at submission time |
| `currency` | `string` | Currency code, e.g. `TZS` |
| `adminInitiated` | `boolean` | `true` if an admin submitted on behalf of organizer |
| `adminId` | `UUID` | Admin's account ID (null if organizer-initiated) |
| `adminNote` | `string` | Admin's reason for initiating (null if organizer-initiated) |
| `organizerNote` | `string` | Optional note from organizer |
| `reviewedById` | `UUID` | ID of admin who reviewed (null if pending) |
| `reviewerName` | `string` | Name of reviewing admin (null if pending) |
| `reviewNote` | `string` | Admin's review note (null if pending) |
| `reviewedAt` | `LocalDateTime` | Timestamp of review (null if pending) |
| `escrowsReleasedCount` | `Integer` | Number of escrow entries released on approval |
| `escrowsSkippedCount` | `Integer` | Escrow entries skipped (e.g. already refunded) |
| `actualReleasedAmount` | `BigDecimal` | Actual amount credited to wallet on approval |
| `initiatedAt` | `LocalDateTime` | When the claim was submitted |
| `updatedAt` | `LocalDateTime` | Last updated timestamp |

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Caller does not have admin role |

---

### 2. Get My Claims (Organizer)

**Endpoint**: `GET /api/v1/e-events/claims/my-claims`  
**Access**: 🔒 Authenticated organizer (any role)  
**Purpose**: Returns all fund claims submitted by the currently authenticated organizer, across all their events.

**No query parameters.**

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "My fund claims retrieved",
  "data": [ /* array of EventFundClaimResponse — same structure as endpoint 1 */ ]
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `401 UNAUTHORIZED` | Invalid or expired JWT |

---

### 3. Get Claim by ID

**Endpoint**: `GET /api/v1/e-events/claims/{claimId}`  
**Access**: 🔒 Authenticated — admin sees any claim; organizer sees only their own  
**Purpose**: Returns full details of a single fund claim.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `claimId` | `UUID` | Yes | The claim's unique identifier |

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Claim retrieved",
  "data": { /* EventFundClaimResponse */ }
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Organizer attempting to view another organizer's claim |
| `404 NOT_FOUND` | Claim with given ID does not exist |

---

### 4. Get Event Claims

**Endpoint**: `GET /api/v1/e-events/claims/event/{eventId}`  
**Access**: 🔒 Authenticated — organizer (own events) or admin (any event)  
**Purpose**: Returns all claims ever submitted for a specific event.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `eventId` | `UUID` | Yes | The event's unique identifier |

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Event claims retrieved",
  "data": [ /* array of EventFundClaimResponse */ ]
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Organizer does not own this event |
| `404 NOT_FOUND` | Event not found |

---

### 5. Get Claimable Amount

**Endpoint**: `GET /api/v1/e-events/claims/event/{eventId}/claimable-amount`  
**Access**: 🔒 Authenticated — organizer (own events) or admin  
**Purpose**: Returns the current claimable amount breakdown for an event, including eligibility status and the active pending claim if one exists.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `eventId` | `UUID` | Yes | The event's unique identifier |

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Claimable amount retrieved",
  "data": {
    "eventId": "uuid",
    "eventTitle": "Dar Jazz Night",
    "totalRevenue": 80000.00,
    "totalRefunded": 5000.00,
    "totalClaimed": 0.00,
    "totalPendingClaims": 0.00,
    "claimableAmount": 60000.00,
    "currency": "TZS",
    "eligible": true,
    "ineligibilityReason": null,
    "activePendingClaimId": null,
    "refundDeadline": "2026-05-10T00:00:00+03:00",
    "pastRefundDeadline": false
  }
}
```

> **Note on claimable amount**: `claimableAmount = totalRevenue − totalRefunded − totalClaimed − totalPendingClaims`. This is a **snapshot estimate** used to indicate eligibility — the `actualReleasedAmount` on approval is the real number, as it reflects the live `HELD` escrow balance at the moment of approval.

**Response Fields**:

| Field | Type | Description |
|-------|------|-------------|
| `totalRevenue` | `BigDecimal` | Net organizer share of all non-refunded tickets |
| `totalRefunded` | `BigDecimal` | Gross amount returned to buyers |
| `totalClaimed` | `BigDecimal` | Already released to organizer wallet |
| `totalPendingClaims` | `BigDecimal` | Locked by current `PENDING` claims |
| `claimableAmount` | `BigDecimal` | What the organizer can claim right now |
| `eligible` | `boolean` | Whether a new claim can be submitted |
| `ineligibilityReason` | `string` | Human-readable reason if `eligible = false` |
| `activePendingClaimId` | `UUID` | ID of the existing pending claim, or `null` |
| `refundDeadline` | `ZonedDateTime` | `eventStartDate − 3 days` |
| `pastRefundDeadline` | `boolean` | `true` = refund window closed, full amount claimable |

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Organizer does not own this event |
| `404 NOT_FOUND` | Event not found |

---

### 6. Get Revenue Summary (Admin)

**Endpoint**: `GET /api/v1/e-events/claims/event/{eventId}/revenue-summary`  
**Access**: 🔒 `ROLE_SUPER_ADMIN` or `ROLE_STAFF_ADMIN`  
**Purpose**: Returns a full financial summary of an event including gross sales, platform fees, refunds, claimed amounts, and escrow balance. Used for admin audit and oversight.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `eventId` | `UUID` | Yes | The event's unique identifier |

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Event revenue summary retrieved",
  "data": {
    "eventId": "uuid",
    "eventTitle": "Dar Jazz Night",
    "grossRevenue": 100000.00,
    "totalRefunded": 5000.00,
    "platformFees": 10000.00,
    "netOrganizerRevenue": 85000.00,
    "totalClaimed": 0.00,
    "totalPendingClaims": 0.00,
    "escrowBalance": 85000.00,
    "currency": "TZS"
  }
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Caller does not have admin role |
| `404 NOT_FOUND` | Event not found |

---

### 7. Submit Claim (Organizer)

**Endpoint**: `POST /api/v1/e-events/claims/event/{eventId}`  
**Access**: 🔒 Authenticated organizer — must own the event  
**Purpose**: Organizer submits a fund claim for the full current claimable amount. The claim amount is computed server-side — organizer cannot override it.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `eventId` | `UUID` | Yes | The event to submit a claim for |

**Request Body** (optional):
```json
{
  "organizerNote": "Requesting first partial claim before event ends"
}
```

**Request Body Fields**:

| Field | Type | Required | Description | Validation |
|-------|------|----------|-------------|------------|
| `organizerNote` | `string` | No | Optional note from the organizer | Max 1000 characters |

**Guard Rules Applied Server-Side**:
- Event must belong to the authenticated organizer
- Event must have ended **OR** `pastRefundDeadline = true` (otherwise only admin can claim)
- `claimableAmount` must be > 0
- No existing `PENDING` claim for this event

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Fund claim submitted successfully",
  "data": { /* EventFundClaimResponse with status: PENDING */ }
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `400 BAD_REQUEST` | A pending claim already exists for this event |
| `400 BAD_REQUEST` | Claimable amount is zero — nothing to claim |
| `400 BAD_REQUEST` | Event has not ended and refund deadline has not passed — only admin can claim |
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Authenticated user does not own this event |
| `404 NOT_FOUND` | Event not found |

---

### 8. Admin-Initiate Claim

**Endpoint**: `POST /api/v1/e-events/claims/event/{eventId}/admin-initiate`  
**Access**: 🔒 `ROLE_SUPER_ADMIN` or `ROLE_STAFF_ADMIN`  
**Purpose**: Admin submits a fund claim on behalf of the organizer. Used when the event is still active (ticket sales ongoing). The claim is flagged `adminInitiated = true` with a mandatory audit note.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `eventId` | `UUID` | Yes | The event to claim funds for |

**Request Body**:
```json
{
  "adminNote": "Organizer requested early release. Event ending tomorrow, no pending refunds."
}
```

**Request Body Fields**:

| Field | Type | Required | Description | Validation |
|-------|------|----------|-------------|------------|
| `adminNote` | `string` | **Yes** | Mandatory reason for admin-initiated claim | Not blank |

**Guard Rules Applied Server-Side**:
- `claimableAmount` must be > 0
- No existing `PENDING` claim for this event
- Claim is capped at `claimableAmount` (never full `totalRevenue`)

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Admin-initiated claim created",
  "data": {
    "claimId": "uuid",
    "claimNumber": "EFC-2026-000003",
    "status": "PENDING",
    "adminInitiated": true,
    "adminId": "uuid-of-admin",
    "adminNote": "Organizer requested early release. Event ending tomorrow, no pending refunds.",
    "claimedAmount": 64000.00,
    "currency": "TZS"
  }
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `400 BAD_REQUEST` | A pending claim already exists for this event |
| `400 BAD_REQUEST` | Claimable amount is zero — nothing to claim |
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Caller does not have admin role |
| `404 NOT_FOUND` | Event not found |
| `422 UNPROCESSABLE_ENTITY` | `adminNote` is blank |

---

### 9. Approve Claim (Admin)

**Endpoint**: `POST /api/v1/e-events/claims/{claimId}/approve`  
**Access**: 🔒 `ROLE_SUPER_ADMIN` or `ROLE_STAFF_ADMIN`  
**Purpose**: Admin approves a pending claim. This atomically acquires a pessimistic write lock on the `EscrowAccount`, fetches **all escrows currently in `HELD` status** for the event, releases every one of them, and credits the full released amount to the organizer's wallet.

> **Design note**: Approval releases ALL `HELD` escrows — not just the `claimedAmount` subset. Each escrow is atomic (one buyer's payment for one checkout session) so partial release has no valid business meaning. The `claimedAmount` on the claim is a snapshot estimate taken at submission time. The `actualReleasedAmount` stored on approval is the authoritative number — it reflects the live `HELD` balance at the moment the admin approves, and may differ if refunds were processed in between.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `claimId` | `UUID` | Yes | The claim to approve |

**Request Body** (optional):
```json
{
  "reviewNote": "Verified escrow balance. Approved for release."
}
```

**Request Body Fields**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `reviewNote` | `string` | No | Optional note from reviewing admin |

> **Critical**: Approval acquires a `PESSIMISTIC_WRITE` lock on the `EscrowAccount` row. Concurrent refund operations for the same event will block until the approval transaction completes, preventing escrow from going negative.

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Fund claim approved and funds released",
  "data": {
    "claimId": "uuid",
    "claimNumber": "EFC-2026-000001",
    "status": "APPROVED",
    "claimedAmount": 50000.00,
    "actualReleasedAmount": 48000.00,
    "escrowsReleasedCount": 12,
    "escrowsSkippedCount": 1,
    "reviewedById": "admin-uuid",
    "reviewerName": "Admin John",
    "reviewNote": "Verified escrow balance. Approved for release.",
    "reviewedAt": "2026-04-27T14:30:00"
  }
}
```

> **Note on `actualReleasedAmount`**: May be less than `claimedAmount` if one or more escrow entries were refunded between claim submission and admin approval. The `escrowsSkippedCount` indicates how many entries were skipped for this reason.

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `400 BAD_REQUEST` | Claim is not in `PENDING` status |
| `400 BAD_REQUEST` | Escrow balance insufficient to cover claim amount |
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Caller does not have admin role |
| `404 NOT_FOUND` | Claim not found |

---

### 10. Reject Claim (Admin)

**Endpoint**: `POST /api/v1/e-events/claims/{claimId}/reject`  
**Access**: 🔒 `ROLE_SUPER_ADMIN` or `ROLE_STAFF_ADMIN`  
**Purpose**: Admin rejects a pending claim. No funds are moved. The organizer may submit a new claim after rejection.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `claimId` | `UUID` | Yes | The claim to reject |

**Request Body** (optional):
```json
{
  "reviewNote": "Pending dispute investigation. Please resubmit after resolution."
}
```

**Request Body Fields**:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `reviewNote` | `string` | No | Reason for rejection (shown to organizer) |

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Fund claim rejected",
  "data": {
    "claimId": "uuid",
    "claimNumber": "EFC-2026-000001",
    "status": "REJECTED",
    "reviewedById": "admin-uuid",
    "reviewerName": "Admin John",
    "reviewNote": "Pending dispute investigation. Please resubmit after resolution.",
    "reviewedAt": "2026-04-27T14:35:00"
  }
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `400 BAD_REQUEST` | Claim is not in `PENDING` status |
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Caller does not have admin role |
| `404 NOT_FOUND` | Claim not found |

---

### 11. Cancel Claim (Organizer)

**Endpoint**: `DELETE /api/v1/e-events/claims/{claimId}`  
**Access**: 🔒 Authenticated organizer — must be the claim owner  
**Purpose**: Organizer cancels their own pending claim before any admin action. This frees up the pending amount, allowing a new claim to be submitted.

**Path Parameters**:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `claimId` | `UUID` | Yes | The claim to cancel |

**No request body.**

**Success Response**:
```json
{
  "success": true,
  "httpStatus": "OK",
  "message": "Fund claim cancelled",
  "data": null
}
```

**Possible Errors**:

| HTTP Status | Description |
|-------------|-------------|
| `400 BAD_REQUEST` | Claim is not in `PENDING` status (already reviewed) |
| `401 UNAUTHORIZED` | Invalid or expired JWT |
| `403 FORBIDDEN` | Organizer does not own this claim |
| `404 NOT_FOUND` | Claim not found |

---

## Financial Safety: Refund & Claim Concurrency

This section explains how the system prevents money from being over-released when a refund and a claim approval happen simultaneously, and why partial escrow release is not supported.

### Why Partial Release Is Not Supported

Each escrow is atomic — it represents one buyer's payment for one checkout session. To release "80% of claimable funds" the service would have to arbitrarily pick specific escrow entries to release and leave others `HELD`. There is no business logic that justifies which individual buyer's payment stays locked — the organizer delivered the event to all of them equally.

```
10 escrows × 950 TZS = 9,500 TZS claimable
"Release 80%" = 7,600 TZS = release 8 escrows, hold 2
→ WHY are those 2 buyers' payments still locked?
→ No valid answer exists at the escrow layer.
```

The correct model is:

```
Claim approval → ALL HELD escrows released → Organizer wallet (full amount lands)
                                                      ↓
                                              Organizer controls staged
                                              payouts via DisbursementService
```

The organizer's wallet is the right layer for staged payouts. The claim domain's job is simply: verify eligibility, get admin approval, atomically convert all `HELD` escrows into wallet balance.

### The Race Condition Risk

```
Revenue = 10,000 TZS in HELD escrows
PENDING claim submitted
→ Refund of 2,000 processed concurrently
→ Claim approved → releases all HELD (now 8,000)
→ Refund deducts 2,000 from already-decremented escrow
→ Escrow goes negative 💀
```

### Protections in Place

| Layer | Mechanism |
|-------|-----------|
| Refund deadline | No refunds allowed within 3 days of event. Eliminates the race entirely post-deadline. |
| Pessimistic DB lock | Both `approveClaim()` and `processRefund()` acquire `PESSIMISTIC_WRITE` lock on the `EscrowAccount` row — serializing the two operations. |
| Balance check | Before any deduction, service verifies `escrow.balance >= amount`. Throws `InsufficientEscrowException` if not. |
| Snapshot vs actual | `claimedAmount` is a snapshot estimate at submission time. `actualReleasedAmount` is the real released amount at approval time — reflects live `HELD` balance after any intervening refunds. |
| `escrowsSkippedCount` | Tracks how many escrow entries were already `REFUNDED` or `DISPUTED` at approval time and therefore skipped. |

### Claimable Amount Formula

```
claimableAmount = totalRevenue − totalRefunded − totalClaimed − totalPendingClaims
```

This formula gives an estimate of what's claimable. The actual amount released on approval equals the live sum of all `HELD` escrow `sellerAmount` values at that moment.

---

## Endpoint Quick Reference

| # | Method | Path | Access | Description |
|---|--------|------|--------|-------------|
| 1 | `GET` | `/claims` | Admin | List all claims (filter by status) |
| 2 | `GET` | `/claims/my-claims` | Organizer | Get my own claims |
| 3 | `GET` | `/claims/{claimId}` | Organizer / Admin | Get single claim by ID |
| 4 | `GET` | `/claims/event/{eventId}` | Organizer / Admin | Get all claims for an event |
| 5 | `GET` | `/claims/event/{eventId}/claimable-amount` | Organizer / Admin | Get claimable amount breakdown |
| 6 | `GET` | `/claims/event/{eventId}/revenue-summary` | Admin | Full event revenue summary |
| 7 | `POST` | `/claims/event/{eventId}` | Organizer | Submit a new claim |
| 8 | `POST` | `/claims/event/{eventId}/admin-initiate` | Admin | Admin-initiate claim for active event |
| 9 | `POST` | `/claims/{claimId}/approve` | Admin | Approve claim + release escrow |
| 10 | `POST` | `/claims/{claimId}/reject` | Admin | Reject claim |
| 11 | `DELETE` | `/claims/{claimId}` | Organizer | Cancel pending claim |

---

## Standard Error Reference

| HTTP Status | Scenario |
|-------------|---------|
| `400 BAD_REQUEST` | Business rule violation (duplicate pending, zero claimable, wrong status) |
| `401 UNAUTHORIZED` | Missing, expired, or malformed JWT |
| `403 FORBIDDEN` | Insufficient role or ownership violation |
| `404 NOT_FOUND` | Event or claim does not exist |
| `422 UNPROCESSABLE_ENTITY` | Validation failure (e.g. blank `adminNote`) |
| `500 INTERNAL_SERVER_ERROR` | Unexpected server error |