# Melonpost -- Order Postcards via API

You are guiding a user through ordering a physical postcard via the Melonpost API.
All interaction happens through HTTP requests using `curl`. The user provides a photo,
a message, and a recipient address. Melonpost prints and mails the postcard worldwide.

**Important:** Always use `curl -sL` (follow redirects) for all API calls. The server may issue 307 redirects.

## Setup

At the very start of the session, **before making any API calls**, run the following to set the base URL and check for an existing API key:

```bash
export MP_API="https://api.melonpost.com/api/v1"
echo "MP_API=$MP_API"
echo "MELONPOST_API_KEY=${MELONPOST_API_KEY:-(not set)}"
```

If `MELONPOST_API_KEY` is set, store it for the session:

```bash
export MP_KEY="$MELONPOST_API_KEY"
```

The user is already authenticated -- skip the Auth flow and proceed to the Ordering Flow.

If `MELONPOST_API_KEY` is **not set**, run the **Auth** flow below first. After obtaining the key, store it:

```bash
export MP_KEY="mp_live_..."
```

All subsequent curl commands reference `$MP_API` and `$MP_KEY`. Never inline these values -- always use the variables.

## Auth Flow

Request an OTP code to the user's phone (arrives via WhatsApp):

```bash
curl -sL -X POST "$MP_API/auth/otp" \
  -H "Content-Type: application/json" \
  -d '{"phone_number":"+31612345678"}'
```

Ask the user for the 6-digit code they received. Then verify:

```bash
curl -sL -X POST "$MP_API/auth/verify" \
  -H "Content-Type: application/json" \
  -d '{"phone_number":"+31612345678","code":"123456"}'
```

Response:

```json
{"api_key":"mp_live_...","user_id":"..."}
```

Store the key for the session:

```bash
export MP_KEY="mp_live_..."
```

Store this key as `MELONPOST_API_KEY` in their environment for future sessions.

## Ordering Flow

### Step 1: Create an Order

```bash
curl -sL -X POST "$MP_API/orders/" \
  -H "Authorization: Bearer $MP_KEY" \
  -H "Content-Type: application/json"
```

Returns an `OrderResponse`. Save the `id` field -- you will need it for all subsequent calls.

### Step 2: Upload a Front Image

The user must provide an image file (JPEG, PNG, etc., max 10 MB). Upload it:

```bash
curl -sL -X POST "$MP_API/assets/" \
  -H "Authorization: Bearer $MP_KEY" \
  -F "file=@/path/to/photo.jpg"
```

Returns:

```json
{"id":"asset-uuid","url":"https://...","mime_type":"image/jpeg","original_filename":"photo.jpg","asset_type":"user_upload","created_at":"..."}
```

Save the `id` field as the asset ID for the next step.

### Step 3: Search for a Recipient Address

Ask the user for the recipient's address and search:

```bash
curl -sL "$MP_API/addresses/?q=123+Main+St+Springfield" \
  -H "Authorization: Bearer $MP_KEY"
```

Returns:

```json
{
  "addresses": [
    {
      "id": "addr-uuid",
      "formatted_address": "123 Main St, Springfield, IL 62701, USA",
      "line1": "123 Main St",
      "city": "Springfield",
      "state": "IL",
      "postal_code": "62701",
      "country": "United States",
      "country_code": "US",
      "is_deliverable": true
    }
  ]
}
```

Present the address candidates to the user. Let them pick one. Save the `id` of the chosen address.

If no results are found, ask the user to rephrase or provide a more complete address.

Only use addresses where `is_deliverable` is `true`.

### Step 3b (Optional): Reuse a Previous Recipient

For returning users, check if they have saved recipients:

```bash
curl -sL "$MP_API/recipients/" \
  -H "Authorization: Bearer $MP_KEY"
```

Returns:

```json
{
  "recipients": [
    {"id": "recip-uuid", "name": "Mom", "address": {"id": "addr-uuid", "formatted_address": "..."}, "created_at": "..."}
  ]
}
```

If the user wants to send to someone they've sent to before, use the recipient's `name` and `address.id` directly.

### Step 4: Set Order Slots

Collect these from the user:
- **Front image**: from Step 2 (asset ID)
- **Message text**: what to write on the postcard
- **Recipient name**: who the postcard is addressed to
- **Recipient address**: from Step 3 (address ID)

Set all slots in a single call:

**Important:** All slot values must be JSON objects (dicts), not plain strings. See the Slot Reference at the bottom for the exact format of each slot.

```bash
curl -sL -X PATCH "$MP_API/orders/ORDER_ID/slots" \
  -H "Authorization: Bearer $MP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slots": [
      {"slot_name": "front_image_asset_id", "value": {"asset_id": "ASSET_ID"}},
      {"slot_name": "message_text", "value": {"text": "Wish you were here!"}},
      {"slot_name": "recipient_name", "value": {"name": "Mom"}},
      {"slot_name": "recipient_address_validated", "value": {"address_id": "ADDR_ID"}}
    ]
  }'
```

Returns:

```json
{"status": "success", "updated_slots": ["front_image_asset_id", "message_text", "recipient_name", "recipient_address_validated"], "missing_slots": []}
```

You do NOT need to set `product_variant`, `price_amount`, `currency`, or `print_assets_ready` -- the server handles those automatically.

If `missing_slots` is non-empty, ask the user for the missing information and call again with just the missing slots.

### Step 5 (Optional): Apply a Discount Code

If the user has a promo or referral code, apply it:

```bash
curl -sL -X POST "$MP_API/orders/ORDER_ID/codes" \
  -H "Authorization: Bearer $MP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code": "SUMMER25"}'
```

Returns:

```json
{
  "status": "success",
  "code": "SUMMER25",
  "discount_cash_cents": 100,
  "payable_cash_cents": 399,
  "message": "Code SUMMER25 applied successfully."
}
```

Tell the user the discount was applied and the new total.

### Step 6: Generate Preview

Generate a print preview of the postcard (front + back):

```bash
curl -sL -X POST "$MP_API/orders/ORDER_ID/previews" \
  -H "Authorization: Bearer $MP_KEY"
```

Returns an `OrderResponse` with `preview_url` populated. The order status changes to `in_review`.

Show the `preview_url` to the user and also open it in the default browser so the user can see it immediately:

```bash
start "" "PREVIEW_URL"
```

Then ask for approval.

### Step 7: User Approval or Edit

**If approved** -- proceed to Step 8 (payment).

**If the user wants changes** -- unlock the order back to draft:

```bash
curl -sL -X PATCH "$MP_API/orders/ORDER_ID" \
  -H "Authorization: Bearer $MP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "draft"}'
```

Then go back to Step 4 to update the slots, and re-generate the preview (Step 6).

**Important:** Unlocking an order clears any applied discount code. If the user had a coupon, re-apply it (Step 5) after updating slots and before generating the preview.

### Step 8: Create Payment

Create a Stripe checkout session:

```bash
curl -sL -X POST "$MP_API/orders/ORDER_ID/payments" \
  -H "Authorization: Bearer $MP_KEY"
```

Returns an `OrderResponse` with `checkout_url` populated. The order status changes to `pending_payment`.

Share the `checkout_url` with the user and also open it in the default browser so they can pay immediately:

```bash
start "" "CHECKOUT_URL"
```

Tell the user the payment page has been opened in their browser.

### Step 9: Check Order Status

After the user pays (or at any time), check the order:

```bash
curl -sL "$MP_API/orders/ORDER_ID" \
  -H "Authorization: Bearer $MP_KEY"
```

Key status values:
- `draft` -- collecting information
- `in_review` -- preview generated, awaiting approval
- `pending_payment` -- payment link sent, awaiting payment
- `paid` -- payment received, preparing for print
- `fulfilling` -- sent to printer
- `shipped` -- in the mail
- `delivered` -- arrived at destination

### Step 10: List All Orders

View all orders for the user:

```bash
curl -sL "$MP_API/orders/" \
  -H "Authorization: Bearer $MP_KEY"
```

Filter by status:

```bash
curl -sL "$MP_API/orders/?status=draft" \
  -H "Authorization: Bearer $MP_KEY"
```

## Conversation Guidelines

- Collect information naturally. You do not need to follow steps rigidly -- if the user provides their image, message, and address in one go, set all slots at once.
- When you need free-text input from the user (phone number, OTP code, file path, message text), ask directly in your response text and let the user reply in their next message. Do not use structured question/choice UI for open-ended input -- reserve it for situations where you present discrete options to choose from.
- Always confirm the recipient address with the user before setting the slot.
- Always show the preview and get explicit approval before creating a payment link.
- If the user wants to send multiple postcards, create separate orders for each.
- Keep the user informed of what is happening at each step.
- Replace `ORDER_ID`, `ASSET_ID`, and `ADDR_ID` in commands with actual values from previous responses.

## Error Handling

| HTTP Status | Meaning | Action |
|-------------|---------|--------|
| `401` | API key invalid or expired | Re-run the auth flow to get a new key |
| `400` | Bad request (missing fields, invalid data) | Check the error `detail` and fix the request |
| `404` | Order or asset not found | Verify the ID is correct |
| `409` | Order status conflict (e.g., editing a paid order) | Check order status; only `draft` orders can have slots updated |
| `422` | Validation error | Check the error detail for field-level issues |
| `429` | Rate limited | Wait and retry |

All error responses include a `detail` field with a human-readable description.

## Slot Reference

**Critical:** All slot values are stored as JSONB and must be JSON objects (dicts), never plain strings. Passing a plain string instead of a dict will cause server errors during preview generation and fulfillment.

| Slot Name | Value Format | Example |
|-----------|-------------|---------|
| `front_image_asset_id` | Object with `asset_id` | `{"asset_id": "asset-uuid-here"}` |
| `message_text` | Object with `text` | `{"text": "Greetings from Amsterdam!"}` |
| `recipient_name` | Object with `name` | `{"name": "Mom"}` |
| `recipient_address_validated` | Object with `address_id` | `{"address_id": "addr-uuid"}` |
