# N1SMS Public API Documentation v1

**Base URL:** `https://n1sms.com/api/mobile`

**Version:** 1.0.0

**Last Updated:** January 2026

---

## Table of Contents

1. [Authentication](#authentication)
2. [Dashboard](#dashboard)
3. [Orders](#orders)
4. [Rentals](#rentals)
5. [Purchases](#purchases)
6. [Countries & Services](#countries--services)
7. [Balance & Transactions](#balance--transactions)
8. [Deposits](#deposits)
9. [Referrals](#referrals)
10. [Notifications](#notifications)
11. [Profile](#profile)
12. [Push Notifications](#push-notifications)

---

## Pricing and currency (all APIs)

All price/cost fields returned by the API (rentals, dashboard prices, stock, orders, etc.) are converted to **GHS using the admin USD→GHS rate** at response time. The primary display amount (`price`, `cost`, `cost_ghs`) is always in GHS. Use `cost_usd` with `symbol_usd` ($) when showing USD; use `cost_ghs` with `symbol_ghs` (₵) when showing GHS. Never display a USD value with the GHS symbol.

**Client timeouts:** Use at least **15 seconds** timeout for list endpoints (countries, services, dashboard, rentals). If the server is under load or the external provider is slow, you may get **503** with a message like "Countries temporarily unavailable. Please try again in a moment." — show that message and offer a retry instead of a generic "Request timeout".

---

## Authentication

All authenticated endpoints require a Bearer token in Authorization header:

```
Authorization: Bearer {token}
```

### Register

**POST** `/api/mobile/auth/register`

Register a new user account.

**Request Body:**
```json
{
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+233XXXXXXXXX",
    "password": "password123",
    "password_confirmation": "password123",
    "referral_code": "OPTIONAL_CODE"
}
```

**Response:**
```json
{
    "success": true,
    "message": "Registration successful. Please verify your phone number.",
    "data": {
        "user_id": 1,
        "requires_verification": true
    }
}
```

**Error Response:**
```json
{
    "success": false,
    "message": "The email has already been taken."
}
```

---

### Send OTP (Registration)

**POST** `/api/mobile/auth/send-otp`

Send OTP for phone verification during registration.

**Request Body:**
```json
{
    "phone": "+233XXXXXXXXX"
}
```

**Response:**
```json
{
    "success": true,
    "message": "OTP sent successfully"
}
```

---

### Verify OTP (Registration)

**POST** `/api/mobile/auth/verify-otp`

Verify OTP during registration.

**Request Body:**
```json
{
    "phone": "+233XXXXXXXXX",
    "otp": "123456"
}
```

**Response:**
```json
{
    "success": true,
    "message": "Phone verified successfully",
    "data": {
        "user_id": 1
    }
}
```

---

### Resend OTP (Registration)

**POST** `/api/mobile/auth/resend-otp`

Resend OTP for registration.

**Request Body:**
```json
{
    "phone": "+233XXXXXXXXX"
}
```

---

### Login

**POST** `/api/mobile/auth/login`

Authenticate user and get access token.

**Request Body:**
```json
{
    "email": "john@example.com",
    "password": "password123"
}
```

**Response:**
```json
{
    "success": true,
    "message": "Login successful",
    "data": {
        "token": "1|abc123...",
        "token_type": "Bearer",
        "expires_at": "2026-02-14T00:00:00.000000Z",
        "user": {
            "id": 1,
            "name": "John Doe",
            "email": "john@example.com",
            "phone": "+233XXXXXXXXX"
        }
    }
}
```

---

### Logout

**POST** `/api/mobile/auth/logout`

🔒 **Requires Authentication**

Invalidate current access token.

**Response:**
```json
{
    "success": true,
    "message": "Logged out successfully"
}
```

---

### Refresh Token

**POST** `/api/mobile/auth/refresh`

🔒 **Requires Authentication**

Refresh access token.

**Response:**
```json
{
    "success": true,
    "data": {
        "token": "2|xyz789...",
        "token_type": "Bearer",
        "expires_at": "2026-02-14T00:00:00.000000Z"
    }
}
```

---

### Forgot Password - Send OTP

**POST** `/api/mobile/auth/forgot-password/send-otp`

Send OTP for password reset.

**Request Body:**
```json
{
    "email": "john@example.com"
}
```

---

### Forgot Password - Verify OTP

**POST** `/api/mobile/auth/forgot-password/verify-otp`

Verify OTP for password reset.

**Request Body:**
```json
{
    "email": "john@example.com",
    "otp": "123456"
}
```

---

### Forgot Password - Reset

**POST** `/api/mobile/auth/forgot-password/reset`

Reset password after OTP verification.

**Request Body:**
```json
{
    "email": "john@example.com",
    "otp": "123456",
    "password": "newpassword123",
    "password_confirmation": "newpassword123"
}
```

---

## Dashboard

### Get Dashboard Data

**GET** `/api/mobile/dashboard`

🔒 **Requires Authentication**

Get user dashboard statistics and recent activity.

**Response:**
```json
{
    "success": true,
    "data": {
        "balance": "150.00",
        "total_purchases": 25,
        "active_purchases": 2,
        "completed_purchases": 20,
        "total_spent": "500.00",
        "recent_activity": [
            {
                "id": 1,
                "order_code": "ABC123",
                "phone_number": "+1234567890",
                "country": "United States",
                "country_code": "US",
                "service": "1688",
                "sms_code": "123456",
                "cost": "15.00",
                "status": "completed",
                "created_at": "2026-01-14T10:00:00.000000Z"
            }
        ],
        "quick_stats": {
            "today_purchases": 3,
            "today_spent": "45.00",
            "this_month_purchases": 25,
            "this_month_spent": "500.00"
        }
    }
}
```

---

### Get Prices (Quick Purchase)

**GET** `/api/mobile/dashboard/prices`

🔒 **Requires Authentication**

Get country prices for a specific service.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| service | string | Yes | Service ID (e.g., "1688") |
| page | int | No | Page number (default: 1) |
| per_page | int | No | Items per page (default: 20, max: 50) |

**Response:**
```json
{
    "success": true,
    "data": {
        "data": [
            {
                "id": 1,
                "name": "United States",
                "short_name": "US",
                "price_usd": "0.50",
                "price_ghs": "6.00"
            }
        ],
        "meta": {
            "current_page": 1,
            "per_page": 20,
            "total": 100,
            "has_more": true
        }
    }
}
```

---

### Get Services

**GET** `/api/mobile/dashboard/services`

🔒 **Requires Authentication**

Get list of available services.

**Response:**
```json
{
    "success": true,
    "data": [
        {
            "id": 1688,
            "name": "One-Time SMS",
            "favourite": true
        },
        {
            "id": 329,
            "name": "Facebook",
            "favourite": false
        }
    ]
}
```

---

## Orders

### List Orders

**GET** `/api/mobile/orders`

🔒 **Requires Authentication**

Get list of user's orders.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| status | string | No | Filter by status (active, pending, completed, expired, cancelled) |
| page | int | No | Page number (default: 1) |
| per_page | int | No | Items per page (default: 20, max: 100) |

**Response:**
```json
{
    "success": true,
    "data": {
        "data": [
            {
                "id": 1,
                "order_code": "ABC123",
                "status": "active",
                "phone_number": "+1234567890",
                "sms_code": null,
                "country": "United States",
                "country_code": "US",
                "service": "1688",
                "cost": "15.00",
                "time_left": 120,
                "created_at": "2026-01-14T10:00:00.000000Z"
            }
        ],
        "meta": {
            "current_page": 1,
            "per_page": 20,
            "total": 50,
            "last_page": 3
        }
    }
}
```

---

### Get Order Details

**GET** `/api/mobile/orders/{order_code}`

🔒 **Requires Authentication**

Get details of a specific order.

**Response:**
```json
{
    "success": true,
    "data": {
        "id": 1,
        "order_code": "ABC123",
        "status": "completed",
        "phone_number": "+1234567890",
        "sms_code": "123456",
        "country": "United States",
        "country_code": "US",
        "service": "1688",
        "cost": "15.00",
        "time_left": null,
        "code_received_at": "2026-01-14T10:05:00.000000Z",
        "created_at": "2026-01-14T10:00:00.000000Z"
    }
}
```

---

### Create Order

**POST** `/api/mobile/orders`

🔒 **Requires Authentication**

Create a new SMS number order.

**Request Body:**
```json
{
    "country": "1",
    "service": "1688",
    "pool": "optional_pool",
    "pricing_option": "0"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| country | string | Yes | Country ID or code |
| service | string | Yes | Service ID |
| pool | string | No | Specific pool to use |
| pricing_option | string | No | "0" for lowest price, "1" for highest quality |

**Response:**
```json
{
    "success": true,
    "message": "Order created successfully",
    "data": {
        "id": 1,
        "order_code": "ABC123",
        "phone_number": "+1234567890",
        "country": "United States",
        "country_code": "US",
        "status": "active",
        "cost": "15.00",
        "new_balance": "135.00",
        "created_at": "2026-01-14T10:00:00.000000Z"
    }
}
```

---

### Cancel Order (refund)

**POST** `/api/v1/orders/{order_code}/cancel` (API key) or **POST** `/api/mobile/purchases/{id}/refund` (Bearer token)

🔒 **Requires Authentication**

Cancel an active order and get a refund. **Rental refund policy:** Refunds for rented numbers are only available when **no messages have been received** and **within 120 minutes of purchase**. Same rules on web, mobile API, and V1 API. Failed eligibility returns 400 with `refund_policy` in the response.

**Response:**
```json
{
    "success": true,
    "message": "Order cancelled and refunded successfully",
    "data": {
        "order_code": "ABC123",
        "status": "cancelled",
        "refunded_amount": "15.00",
        "new_balance": "150.00"
    }
}
```

---

### Resend SMS

**POST** `/api/mobile/orders/{order_code}/resend`

🔒 **Requires Authentication**

Request a new SMS code for a completed order (costs same as original order).

**Response:**
```json
{
    "success": true,
    "message": "SMS resend requested successfully",
    "data": {
        "order_code": "ABC123",
        "status": "active",
        "cost": "15.00",
        "new_balance": "120.00"
    }
}
```

---

## Rentals

Rentals are long-term phone numbers. Use **List rental options** to get countries, durations, and product keys; then **Purchase rental** with a `rental_product_id`. Manage the rental (view number, expiry, incoming SMS) via **Get Purchase Details** and **Refresh Purchase** using the returned `purchase_id`.

**Prices:** The API returns the **converted GHS amount** in each product’s **`price`** (and **`cost_ghs`**) field. No conversion is needed in Flutter — just display **`price`** with the GHS symbol (e.g. GH₵ 90.90).

### List Rental Options

**GET** `/api/mobile/rentals`

🔒 **Requires Authentication**

Get rental options: countries (rental types), durations per country, and products (price/stock) keyed by `{rental_id}_{days}`.

**Response:**
```json
{
    "success": true,
    "data": {
        "balance": 150.00,
        "countries": [
            { "code": "11", "rental_id": "11", "name": "Canada Virtual", "flag_code": "ca" },
            { "code": "15", "rental_id": "15", "name": "United Kingdom", "flag_code": "gb" }
        ],
        "durations_by_rental": {
            "11": [1, 7, 30],
            "15": [7, 30]
        },
        "products_by_key": {
            "11_7": {
                "id": 1,
                "price": 42.00,
                "price_currency": "GHS",
                "cost_ghs": 42.00,
                "cost_usd": 3.50,
                "currency_ghs": "GHS",
                "currency_usd": "USD",
                "symbol_ghs": "₵",
                "symbol_usd": "$",
                "stock": 100,
                "rental_type_name": "Canada Virtual",
                "days": 7,
                "country_name": "Canada Virtual",
                "country_code": "CA"
            }
        },
        "exchange_rate": 12.0,
        "currency_display": "Display cost_ghs with symbol_ghs (₵) for GHS; cost_usd with symbol_usd ($) for USD. Do not show USD amount with GHS symbol.",
        "usage": {
            "step1": "Use country.code (or country.rental_id) as the rental key - numeric id like \"11\", NOT \"CA\".",
            "step2": "Durations: durations_by_rental[selected_rental_id]",
            "step3": "Product key = selected_rental_id + \"_\" + selected_days e.g. \"11_7\"",
            "step4": "Product = products_by_key[product_key]; use product.id as rental_product_id for purchase.",
            "step5": "Display amount as product.price_currency + product.price (e.g. GH₵ 42.00). Do NOT use cost_usd with GHS symbol."
        }
    }
}
```

**Flutter / Mobile: How to use (API gives converted amount straight)**

- The API **already returns the amount in GHS**. You do **not** convert on the client.
- For each rental plan row, get the product from `data.products_by_key[productKey]`.
- **Display the price as:** `product['price_currency']` + `product['price']`  
  Example: `"GH₵ ${product['price']}"` → e.g. **GH₵ 90.90**
- Use **`product['price']`** (or **`product['cost_ghs']`** — same value). **Do not** use `product['cost_usd']` for the main label or you will show dollar amounts with the GHS symbol.
- To purchase, send **`product['id']`** as **`rental_product_id`** in **POST** `/api/mobile/rentals/purchase`.

**How to use (important – prices/days must match selected country):**

1. **Selected “country”** in the UI is a **rental option**. Store **`country.code`** (or `country.rental_id`) as the **rental id** – e.g. `"11"`.  
   Do **not** use `country_code` (e.g. `"CA"`) or `name` for lookups; they are not keys.

2. **Durations for that selection:**  
   `durations = data.durations_by_rental[selected_rental_id]`  
   Example: `durations_by_rental["11"]` → `[1, 7, 30]`.

3. **Product key** = **rental id + `"_"` + selected days** (string):  
   `product_key = selected_rental_id + "_" + selected_days`  
   Example: for Canada Virtual (code `"11"`) and 7 days → `"11_7"`.

4. **Product** = `data.products_by_key[product_key]`.  
   Use **`product.id`** as **`rental_product_id`** in the purchase request.

**Example:** User selects “Canada Virtual” and 7 days. From `countries` get `code: "11"`. Then `durations_by_rental["11"]` → `[1, 7, 30]`; key = `"11_7"`; `products_by_key["11_7"]` → `{ id: 1, cost_ghs: 42, days: 7, ... }`. Send **`rental_product_id: 1`** to purchase.

The response also includes a **`usage`** object with these steps for reference.

**Rental plan display (important):** Each product has a **`price`** field that is **already in GHS** (converted using the admin rate). Use **`price`** and **`price_currency`** (GHS) for the main display—e.g. show **GH₵** + **price** (e.g. GH₵ 72.00). Do **not** use **cost_usd** with the GHS symbol or you will show dollar amounts as if they were cedis. The **`exchange_rate`** in the response is the admin USD→GHS rate used to compute **price** / **cost_ghs** from **cost_usd**.

---

### Purchase Rental (how to buy)

**POST** `/api/mobile/rentals/purchase`

🔒 **Requires Authentication** (Bearer token)

**How to buy (step by step):**

1. **List options:** **GET** `/api/mobile/rentals` → get `data.countries`, `data.durations_by_rental`, `data.products_by_key`, `data.balance`.
2. **User picks:** a country (e.g. "United States") and a duration (e.g. 7 days). Use `country.code` (the rental id, e.g. `"13"`) and the selected `days`.
3. **Product key:** `productKey = "${country.code}_${days}"` (e.g. `"13_7"`).
4. **Product:** `product = data.products_by_key[productKey]`. Show price as **GH₵** + `product.price`.
5. **Buy:** **POST** `/api/mobile/rentals/purchase` with body **`{ "rental_product_id": product.id }`**.
6. **Success:** Response has `data.purchase_id`, `data.phone_number`, `data.expires_at`. Use **GET** `/api/mobile/purchases/{purchase_id}` to view the rental and incoming SMS.

**Request Body:**
```json
{
    "rental_product_id": 1
}
```

**Response:**
```json
{
    "success": true,
    "message": "Rental purchased successfully",
    "data": {
        "purchase_id": 123,
        "order_code": "CrKaTNZN",
        "rental_code": "CrKaTNZN",
        "phone_number": "+12246589245",
        "country": "Canada Virtual",
        "country_code": "CA",
        "cost": "42.00",
        "expires_at": "2026-04-01T12:00:00.000000Z",
        "expires_in_seconds": 2592000,
        "status": "active"
    }
}
```

Use `purchase_id` with **GET** `/api/mobile/purchases/{id}` and **POST** `/api/mobile/purchases/{id}/refresh` to get/update the rental (phone number, expiry, incoming SMS messages). For rentals, the purchase detail and refresh responses include `order_type: "rental"`, `expires_at`, `expires_in_seconds`, and `messages` (incoming SMS list; sender shown as N1SMS).

---

## Purchases

### List Purchases

**GET** `/api/mobile/purchases`

🔒 **Requires Authentication**

Get list of all purchases (one-time SMS and rentals) with statistics. Each item includes `order_type` (`"one-time"` or `"rental"`). For **rentals**, each item also includes a **`refund`** object: `refund_policy`, `refund_available_until`, and `can_refund` (time-based only; exact eligibility is in GET purchase details).

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| status | string | No | Filter by status (all, active, pending, completed, expired, cancelled) |
| page | int | No | Page number (default: 1) |
| per_page | int | No | Items per page (default: 20, max: 100) |

**Response:**
```json
{
    "success": true,
    "data": {
        "data": [
            {
                "id": 1,
                "order_code": "ABC123",
                "status": "completed",
                "phone_number": "+1234567890",
                "sms_code": "123456",
                "country": "United States",
                "country_code": "US",
                "service": "1688",
                "cost": "15.00",
                "time_left": null,
                "created_at": "2026-01-14T10:00:00.000000Z",
                "code_received_at": "2026-01-14T10:05:00.000000Z"
            }
        ],
        "stats": {
            "total": 50,
            "active": 2,
            "completed": 45
        },
        "meta": {
            "current_page": 1,
            "per_page": 20,
            "total": 50,
            "last_page": 3
        }
    }
}
```

---

### Get Purchase Details

**GET** `/api/mobile/purchases/{id}`

🔒 **Requires Authentication**

Get details of a specific purchase (also refreshes status from API). For **rentals**, the response includes `order_type: "rental"`, `rental_code`, `expires_at`, `expires_in_seconds`, `messages`, and a **`refund`** object: `refund_policy` (full policy text), `refund_available_until` (ISO timestamp; 120 minutes after purchase), `can_refund` (true only if status is active/pending, within 120 min, and no messages received), and `message_count`. Use `can_refund` to show or hide a "Request refund" button.

**Response:**
```json
{
    "success": true,
    "data": {
        "id": 1,
        "order_code": "ABC123",
        "status": "completed",
        "phone_number": "+1234567890",
        "sms_code": "123456",
        "country": "United States",
        "country_code": "US",
        "service": "1688",
        "pool": null,
        "cost": "15.00",
        "time_left": null,
        "created_at": "2026-01-14T10:00:00.000000Z",
        "code_received_at": "2026-01-14T10:05:00.000000Z"
    }
}
```

---

### Refresh Purchase Status

**POST** `/api/mobile/purchases/{id}/refresh`

🔒 **Requires Authentication**

Manually refresh purchase status from provider. For **rentals**, this fetches latest rental info and messages; response includes `phone_number`, `expires_at`, `expires_in_seconds`, and `messages` (incoming SMS). For one-time orders, returns updated `sms_code` and `time_left`.

**Response:**
```json
{
    "success": true,
    "message": "Purchase status refreshed",
    "data": {
        "id": 1,
        "order_code": "ABC123",
        "status": "completed",
        "sms_code": "123456",
        "phone_number": "+1234567890",
        "time_left": null
    }
}
```

---

### Refund Purchase

**POST** `/api/mobile/purchases/{id}/refund`

🔒 **Requires Authentication**

Cancel and refund an active purchase. Same rules apply on web and all APIs (mobile and V1).

**Rental refund policy (long-term rented numbers):** Refunds are only available for rentals that have **not received any messages**, and only **within 120 minutes of purchase**. If either condition is not met, the API returns 400 with `refund_policy` in the response. Use **GET** `/api/mobile/purchases/{id}` to check `refund.can_refund`, `refund.refund_available_until`, and `refund.message_count` before showing a "Request refund" button.

**Response:**
```json
{
    "success": true,
    "message": "Order cancelled and refunded successfully",
    "data": {
        "order_code": "ABC123",
        "status": "cancelled",
        "refunded_amount": "15.00",
        "new_balance": "150.00"
    }
}
```

**Error (rental, e.g. past 120 min or messages received):** 400 with message and `refund_policy`: *"Refunds are only available for rentals that have not received any messages. You can only refund within 120 minutes of purchase."*

---

### Resend Purchase SMS

**POST** `/api/mobile/purchases/{id}/resend`

🔒 **Requires Authentication**

Request a new SMS code for a completed purchase.

**Response:**
```json
{
    "success": true,
    "message": "SMS resend requested successfully",
    "data": {
        "order_code": "ABC123",
        "status": "active",
        "cost": "15.00",
        "new_balance": "120.00"
    }
}
```

---

### Delete Purchase

**DELETE** `/api/mobile/purchases/{id}`

🔒 **Requires Authentication**

Remove a completed/expired/cancelled purchase from history.

**Response:**
```json
{
    "success": true,
    "message": "Order removed from purchase history"
}
```

---

## Countries & Services

### Get Countries

**GET** `/api/mobile/countries`

🔒 **Requires Authentication**

Get list of available countries.

**Response:**
```json
{
    "success": true,
    "data": [
        {
            "id": 1,
            "name": "United States",
            "short_name": "US",
            "cc": "1"
        },
        {
            "id": 2,
            "name": "United Kingdom",
            "short_name": "GB",
            "cc": "44"
        }
    ]
}
```

---

### Get Services

**GET** `/api/mobile/services`

🔒 **Requires Authentication**

Get list of available services.

**Response:**
```json
{
    "success": true,
    "data": [
        {
            "id": 1688,
            "name": "One-Time SMS"
        },
        {
            "id": 329,
            "name": "Facebook"
        },
        {
            "id": 1012,
            "name": "WhatsApp"
        }
    ]
}
```

---

### Get Stock

**GET** `/api/mobile/stock`

🔒 **Requires Authentication**

Get stock availability for a country and service.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| country | string | Yes | Country ID or code |
| service | string | Yes | Service ID |

**Response:**
```json
{
    "success": true,
    "data": {
        "amount": 1500,
        "price": "0.50"
    }
}
```

---

## Balance & Transactions

### Get Balance

**GET** `/api/mobile/balance`

🔒 **Requires Authentication**

Get user's current balance.

**Response:**
```json
{
    "success": true,
    "data": {
        "balance": "150.00",
        "currency": "GHS"
    }
}
```

---

### Get Transactions

**GET** `/api/mobile/transactions`

🔒 **Requires Authentication**

Get user's transaction history.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| type | string | No | Filter by type (deposit, purchase, refund, withdrawal) |
| page | int | No | Page number (default: 1) |
| per_page | int | No | Items per page (default: 20, max: 100) |

**Response:**
```json
{
    "success": true,
    "data": {
        "data": [
            {
                "id": 1,
                "type": "deposit",
                "amount": "100.00",
                "description": "Paystack deposit",
                "status": "completed",
                "created_at": "2026-01-14T09:00:00.000000Z"
            },
            {
                "id": 2,
                "type": "purchase",
                "amount": "-15.00",
                "description": "SMS Number Purchase",
                "status": "completed",
                "created_at": "2026-01-14T10:00:00.000000Z"
            }
        ],
        "meta": {
            "current_page": 1,
            "per_page": 20,
            "total": 100,
            "last_page": 5
        }
    }
}
```

---

## Deposits

### Get Deposits

**GET** `/api/mobile/deposits`

🔒 **Requires Authentication**

Get user's deposit history.

**Response:**
```json
{
    "success": true,
    "data": [
        {
            "id": 1,
            "amount": "100.00",
            "method": "paystack",
            "status": "completed",
            "reference": "PSK_ABC123",
            "created_at": "2026-01-14T09:00:00.000000Z"
        }
    ]
}
```

---

### Initialize Paystack Payment

**POST** `/api/mobile/deposits/paystack/initialize`

🔒 **Requires Authentication**

Initialize a Paystack payment.

**Request Body:**
```json
{
    "amount": 100
}
```

**Response:**
```json
{
    "success": true,
    "data": {
        "reference": "PSK_ABC123"
    }
}
```

---

### Check Deposit Status

**POST** `/api/mobile/deposits/{id}/check-status`

🔒 **Requires Authentication**

Check status of a deposit.

**Response:**
```json
{
    "success": true,
    "data": {
        "id": 1,
        "status": "completed",
        "amount": "100.00",
        "new_balance": "250.00"
    }
}
```

---

## Referrals

### Get Referral Info

**GET** `/api/mobile/referrals`

🔒 **Requires Authentication**

Get user's referral information and earnings.

**Response:**
```json
{
    "success": true,
    "data": {
        "referral_code": "JOHN123",
        "referral_link": "https://n1sms.com/register?ref=JOHN123",
        "total_referrals": 10,
        "total_earnings": "50.00",
        "pending_earnings": "10.00",
        "withdrawn_earnings": "40.00",
        "referrals": [
            {
                "id": 1,
                "name": "Jane Doe",
                "joined_at": "2026-01-10T00:00:00.000000Z",
                "earnings": "5.00"
            }
        ]
    }
}
```

---

### Request Withdrawal

**POST** `/api/mobile/referrals/withdraw`

🔒 **Requires Authentication**

Request withdrawal of referral earnings.

**Request Body:**
```json
{
    "amount": 50
}
```

**Response:**
```json
{
    "success": true,
    "message": "Withdrawal request submitted successfully",
    "data": {
        "amount": "50.00",
        "status": "pending"
    }
}
```

---

## Notifications

### Get Notifications

**GET** `/api/mobile/notifications`

🔒 **Requires Authentication**

Get user's notifications.

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| page | int | No | Page number (default: 1) |
| per_page | int | No | Items per page (default: 20) |

**Response:**
```json
{
    "success": true,
    "data": {
        "data": [
            {
                "id": "abc123",
                "title": "SMS Code Received",
                "message": "Your SMS code for order ABC123 is: 123456",
                "read": false,
                "created_at": "2026-01-14T10:05:00.000000Z"
            }
        ],
        "meta": {
            "current_page": 1,
            "per_page": 20,
            "total": 50
        }
    }
}
```

---

### Get Recent Notifications

**GET** `/api/mobile/notifications/recent`

🔒 **Requires Authentication**

Get recent unread notifications.

**Response:**
```json
{
    "success": true,
    "data": [
        {
            "id": "abc123",
            "title": "SMS Code Received",
            "message": "Your SMS code for order ABC123 is: 123456",
            "created_at": "2026-01-14T10:05:00.000000Z"
        }
    ]
}
```

---

### Get Unread Count

**GET** `/api/mobile/notifications/count`

🔒 **Requires Authentication**

Get count of unread notifications.

**Response:**
```json
{
    "success": true,
    "data": {
        "count": 5
    }
}
```

---

### Mark Notification as Read

**POST** `/api/mobile/notifications/{id}/read`

🔒 **Requires Authentication**

Mark a specific notification as read.

**Response:**
```json
{
    "success": true,
    "message": "Notification marked as read"
}
```

---

### Mark All as Read

**POST** `/api/mobile/notifications/read-all`

🔒 **Requires Authentication**

Mark all notifications as read.

**Response:**
```json
{
    "success": true,
    "message": "All notifications marked as read"
}
```

---

## Profile

### Get Profile

**GET** `/api/mobile/profile`

🔒 **Requires Authentication**

Get user's profile information.

**Response:**
```json
{
    "success": true,
    "data": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "phone": "+233XXXXXXXXX",
        "email_verified": true,
        "phone_verified": true,
        "two_factor_enabled": false,
        "created_at": "2026-01-01T00:00:00.000000Z"
    }
}
```

---

### Update Profile

**PUT** `/api/mobile/profile`

🔒 **Requires Authentication**

Update user's profile information.

**Request Body:**
```json
{
    "name": "John Smith",
    "email": "johnsmith@example.com",
    "phone": "+233XXXXXXXXX"
}
```

**Response:**
```json
{
    "success": true,
    "message": "Profile updated successfully",
    "data": {
        "id": 1,
        "name": "John Smith",
        "email": "johnsmith@example.com",
        "phone": "+233XXXXXXXXX"
    }
}
```

---

### Change Password

**POST** `/api/mobile/profile/password`

🔒 **Requires Authentication**

Change user's password.

**Request Body:**
```json
{
    "current_password": "oldpassword123",
    "password": "newpassword123",
    "password_confirmation": "newpassword123"
}
```

**Response:**
```json
{
    "success": true,
    "message": "Password changed successfully"
}
```

---

### Update Notification Settings

**POST** `/api/mobile/profile/notifications`

🔒 **Requires Authentication**

Update notification preferences.

**Request Body:**
```json
{
    "email_notifications": true,
    "sms_notifications": true,
    "push_notifications": true
}
```

**Response:**
```json
{
    "success": true,
    "message": "Notification settings updated"
}
```

---

### Update 2FA Settings

**POST** `/api/mobile/profile/2fa`

🔒 **Requires Authentication**

Enable or disable two-factor authentication.

**Request Body:**
```json
{
    "enabled": true
}
```

**Response:**
```json
{
    "success": true,
    "message": "Two-factor authentication enabled",
    "data": {
        "secret": "JBSWY3DPEHPK3PXP",
        "qr_code": "data:image/png;base64,..."
    }
}
```

---

## Push Notifications

### Register Device

**POST** `/api/mobile/push/register`

🔒 **Requires Authentication**

Register a device for push notifications.

**Request Body:**
```json
{
    "token": "fcm_device_token_here",
    "platform": "android"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| token | string | Yes | FCM device token |
| platform | string | Yes | "android" or "ios" |

**Response:**
```json
{
    "success": true,
    "message": "Device registered for push notifications"
}
```

---

### Unregister Device

**POST** `/api/mobile/push/unregister`

🔒 **Requires Authentication**

Unregister a device from push notifications.

**Request Body:**
```json
{
    "token": "fcm_device_token_here"
}
```

**Response:**
```json
{
    "success": true,
    "message": "Device unregistered from push notifications"
}
```

---

## Error Responses

All endpoints return errors in a consistent format:

```json
{
    "success": false,
    "message": "Error description",
    "errors": {
        "field_name": ["Validation error message"]
    }
}
```

### HTTP Status Codes

| Code | Description |
|------|-------------|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request (validation error) |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 422 | Unprocessable Entity |
| 429 | Too Many Requests |
| 500 | Internal Server Error |

---

## Quick Service IDs Reference

| Service | ID |
|---------|-----|
| One-Time SMS | 1688 |
| WhatsApp | 1012 |
| Facebook | 329 |
| Instagram | 457 |
| Telegram | 907 |
| TikTok | 924 |
| Twitter/X | 948 |
| YouTube | 1227 |
| Snapchat | 846 |
| Discord | 273 |
| Google | 395 |
| LinkedIn | 523 |
| Netflix | 630 |
| Spotify | 861 |

---

## Public API v1 (API Key)

The same rental and order flows are available for server integrations using an **API key**.

**Base URL:** `https://n1sms.com/api/v1`  
**Authentication:** `Authorization: Bearer {api_key}` or header `X-API-Key: {api_key}`

### Rentals (API Key)

- **GET** `/api/v1/rentals` — List rental options (same shape as **GET** `/api/mobile/rentals`: `balance`, `countries`, `durations_by_rental`, `products_by_key`).
- **POST** `/api/v1/rentals` — Purchase a rental. Body: `{ "rental_product_id": 1 }`. Same response shape as mobile **POST** `/api/mobile/rentals/purchase` (throttle: 30/min).

### Orders (API Key)

- **GET** `/api/v1/orders` — List orders. Query `type=rental` or `type=one-time` to filter. Each item includes `order_type`, `expires_at` for rentals.
- **GET** `/api/v1/orders/{order_code}` — Order/rental details. For rentals, response includes `order_type: "rental"`, `rental_code`, `expires_at`, `expires_in_seconds`, `messages`, and **`refund`** (`refund_policy`, `refund_available_until`, `can_refund`, `message_count`) for refund eligibility.
- **POST** `/api/v1/orders/{order_code}/cancel` — Cancel order and refund. For rentals: only within 120 minutes of purchase and when no messages have been received (same policy as mobile and web).

Rental codes (order_code for rentals) can be polled via **GET** `/api/v1/orders/{order_code}` to get updated `phone_number`, `expires_at`, and `messages` (new incoming SMS/codes).

---

## Rate Limiting

API requests are rate limited to prevent abuse:

- **Authenticated requests:** 60 requests per minute
- **Authentication endpoints:** 10 requests per minute

When rate limited, the API returns a 429 status code:

```json
{
    "success": false,
    "message": "Too many requests. Please try again later."
}
```

---

## Support

For API support, please contact:
- **Email:** support@n1sms.com
- **Phone:** 0544851594
- **WhatsApp:** 0544851594
- **Website:** https://n1sms.com

---

## Getting Started

### 1. Create Account

Register a new account to get your API credentials:

```bash
curl -X POST https://n1sms.com/api/mobile/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+233XXXXXXXXX",
    "password": "password123",
    "password_confirmation": "password123"
  }'
```

### 2. Login to Get Token

Authenticate to receive your access token:

```bash
curl -X POST https://n1sms.com/api/mobile/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john@example.com",
    "password": "password123"
  }'
```

### 3. Use the Token

Include your token in the Authorization header for authenticated requests:

```bash
curl -X GET https://n1sms.com/api/mobile/dashboard \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
```

---

## Common Use Cases

### Purchase SMS Number

```bash
# Get available countries and services
curl -X GET "https://n1sms.com/api/mobile/countries" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"

# Purchase number
curl -X POST https://n1sms.com/api/mobile/orders \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "country": "1",
    "service": "1688"
  }'
```

### Check Order Status

```bash
curl -X GET https://n1sms.com/api/mobile/orders/ABC123 \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
```

### Get Balance

```bash
curl -X GET https://n1sms.com/api/mobile/balance \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
```
