# Claim Offer
> Source: /api-reference/members/loyalty/claim-offer
> Purchase an offer with points and create a fulfillment
> Endpoint: POST /members/me/loyalty/offers/{offerId}/claim

## Overview [#overview]

Claims an offer for the authenticated member by deducting the required points from their wallet and creating a fulfillment with verification codes. This action is irreversible once completed.

## Authentication [#authentication]

Requires a valid Clerk JWT token in the Authorization header.

## Path Parameters [#path-parameters]

<ParamField path="offerId" type="string" required>
  Offer document ID to claim
</ParamField>

## Request Body [#request-body]

<ParamField body="propertyId" type="string">
  Property document ID where offer will be redeemed. Required for property-specific offers.
</ParamField>

<ParamField body="reservationId" type="string">
  Reservation external ID for in-stay offers (from core.reservations.external_id)
</ParamField>

<RequestExample>

```bash cURL
curl -X POST "http://localhost:3000/v1/members/me/loyalty/offers/offer_spa_discount/claim" \
  -H "Authorization: Bearer YOUR_CLERK_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "propertyId": "prop_luxury_resort",
    "reservationId": "RES-12345"
  }'
```

```typescript TypeScript
const offerId = "offer_spa_discount";
const response = await fetch(`http://localhost:3000/v1/members/me/loyalty/offers/${offerId}/claim`, {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_CLERK_JWT',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    propertyId: "prop_luxury_resort", 
    reservationId: "RES-12345"
  })
});

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

</RequestExample>

## Response [#response]

<ResponseField name="success" type="boolean" required>
  Whether the offer was successfully claimed
</ResponseField>

<ResponseField name="fulfillmentId" type="string">
  UUID of the created fulfillment record
</ResponseField>

<ResponseField name="redemptionType" type="string">
  How this offer should be redeemed
</ResponseField>

<ResponseField name="verificationCode" type="string">
  Primary verification code for redeeming at property
</ResponseField>

<ResponseField name="activationCode" type="string">
  Secondary activation code if needed
</ResponseField>

<ResponseField name="qrCodeUrl" type="string">
  URL to QR code for mobile scanning
</ResponseField>

<ResponseField name="pointsCharged" type="number">
  Number of points deducted from wallet
</ResponseField>

<ResponseField name="status" type="string">
  Initial status of the fulfillment (usually "claimed")
</ResponseField>

<ResponseField name="claimedAt" type="string" required>
  ISO 8601 timestamp when offer was claimed
</ResponseField>

<ResponseField name="responseDeadline" type="string">
  ISO 8601 timestamp for response deadline (for approval-based offers)
</ResponseField>

<ResponseExample>

```json 200 Response - Success
{
  "success": true,
  "fulfillmentId": "fulfill_550e8400-e29b-41d4-a716-446655440000",
  "redemptionType": "propertySpecific",
  "verificationCode": "SPA-DISC-2024-789",
  "activationCode": null,
  "qrCodeUrl": "https://cdn.journey.com/qr/fulfill_550e8400.png",
  "pointsCharged": 1500,
  "status": "claimed",
  "claimedAt": "2024-04-15T14:30:00Z",
  "responseDeadline": null
}
```

```json 200 Response - Approval Required
{
  "success": true,
  "fulfillmentId": "fulfill_550e8400-e29b-41d4-a716-446655440001", 
  "redemptionType": "propertySpecific",
  "verificationCode": null,
  "pointsCharged": 2500,
  "status": "pending_approval",
  "claimedAt": "2024-04-15T14:30:00Z",
  "responseDeadline": "2024-04-17T14:30:00Z"
}
```

```json 400 Response - Insufficient Points
{
  "statusCode": 400,
  "message": "Insufficient points. Required: 1500, Available: 1200"
}
```

```json 400 Response - Not Eligible
{
  "statusCode": 400,
  "message": "Offer cannot be claimed: Requires active reservation"
}
```

```json 404 Response - Not Found
{
  "statusCode": 404,
  "message": "Offer not found"
}
```

</ResponseExample>

## Claim Process [#claim-process]

### Validation [#validation]
1. **Offer Existence**: Verifies offer exists and is active
2. **Member Eligibility**: Checks tier level, points balance, property access
3. **Property Context**: Validates property and reservation IDs if required
4. **Time Windows**: Ensures claim is within valid time windows

### Point Deduction [#point-deduction]
1. **Immediate**: Points are deducted from wallet immediately
2. **Transaction Record**: Creates a DEBIT transaction in wallet history
3. **Balance Update**: Wallet balance is updated in real-time

### Fulfillment Creation [#fulfillment-creation]
1. **Fulfillment Record**: Creates a fulfillment with unique ID
2. **Verification Codes**: Generates codes for property redemption
3. **Status Setting**: Initial status based on redemption type
4. **Deadline Setting**: Sets response deadlines for approval-based offers

## Fulfillment Statuses [#fulfillment-statuses]

### claimed [#claimed]
- **Meaning**: Recently claimed, being processed
- **Next Step**: Will move to "approved" or require property approval
- **Codes**: May not be immediately available

### pending_approval [#pending_approval]
- **Meaning**: Waiting for property approval
- **Next Step**: Property will approve/reject or modify
- **Codes**: Not yet available until approved

### approved [#approved]
- **Meaning**: Ready to use at property
- **Next Step**: Member redeems using verification code
- **Codes**: Available for redemption

## Property Context Requirements [#property-context-requirements]

### Property-Specific Offers [#property-specific-offers]
- **propertyId**: Required - specifies where offer will be redeemed
- **reservationId**: Optional - links to specific stay

### In-Stay Offers [#in-stay-offers]  
- **propertyId**: Required - must match reservation property
- **reservationId**: Required - must be member's active/future reservation

### Global Offers [#global-offers]
- **propertyId**: Not required - can be used anywhere
- **reservationId**: Not required - not tied to specific stay

## Error Scenarios [#error-scenarios]

### Insufficient Points [#insufficient-points]
- **Check**: Use [Get Balance](/api-reference/members/wallet/get-balance) first
- **Resolution**: Member needs to earn more points or choose cheaper offer

### Property Access [#property-access]
- **Check**: Ensure member has reservation at specified property
- **Resolution**: Provide correct property/reservation IDs

### Offer Restrictions [#offer-restrictions]
- **Check**: Review offer [eligibility details](/api-reference/members/loyalty/get-offer)
- **Resolution**: Satisfy restriction requirements (e.g., active stay)

### Time Windows [#time-windows]
- **Check**: Some offers have check-in time restrictions
- **Resolution**: Claim within valid time window

## Related Endpoints [#related-endpoints]

- [Get Offer Details](/api-reference/members/loyalty/get-offer) - Check eligibility before claiming
- [Get Fulfillments](/api-reference/members/wallet/get-fulfillments) - View claimed offers
- [Get Wallet Balance](/api-reference/members/wallet/get-balance) - Check points balance
