# API Data Exchange & Contract Specification

## 1. Overview
This document serves as the technical contract for the two-way data exchange between the **Property Financing Platform** and the **Partner ERP System**. It defines the exact HTTP endpoints, methods, headers, authentication mechanisms, request payloads, and expected responses required for integration.

---

## 2. Environments and Base URLs

| Environment | Platform Base URL | Partner ERP Base URL (Placeholder) |
| :--- | :--- | :--- |
| **Sandbox / UAT** | `https://sandbox-api.miliki.com/api/integrations/v1` | `https://sandbox.partner-erp.com/api/v1` |
| **Production** | `https://api.miliki.com/api/integrations/v1` | `https://api.partner-erp.com/api/v1` |

---

## 3. Authentication & Security

All traffic must be secured via **HTTPS/TLS**. 

### 3.1 Platform → ERP (Synchronous API Calls)
When the Platform calls the ERP's REST APIs (e.g., locking a plot), requests are authenticated via an API Key provided by the ERP.

**Standard Request Headers:**
```http
Content-Type: application/json
Accept: application/json
Authorization: Bearer <ERP_PROVIDED_API_KEY>
X-Correlation-ID: <UUID_FOR_TRACING>
```

### 3.2 Webhook Security (Both Directions)
All asynchronous webhooks (sent by ERP to Platform, or Platform to ERP) are secured via **HMAC-SHA256 signatures** to prevent tampering and replay attacks. 

The sender signs the payload using a shared secret.

**Webhook Request Headers:**
```http
Content-Type: application/json
Accept: application/json
X-Webhook-Signature: <HMAC_SHA256_HASH>
```
*Signature Calculation:* `HMAC-SHA256(timestamp + "." + raw_request_body, webhook_secret)`

---

## 4. Standardized Webhook Envelope

All asynchronous webhook payloads follow a standardized envelope to ensure idempotency and reliable processing.

```json
{
  "event_id": "evt_909090", // Unique identifier for this specific event
  "event_type": "project.created", // Action that occurred
  "timestamp": "2026-08-19T07:30:00Z", // ISO 8601 UTC timestamp
  "data": {
    // Event specific payload goes here
  }
}
```

---

## 5. ERP → Platform: Incoming Webhooks

The ERP pushes data to the Platform when property inventory is created or updated.

### 5.1 Project Synchronization
**Triggered when:** A new property project/estate is created or updated in the ERP.
**Endpoint (Platform):** `POST /webhooks/erp/events`

**Payload:**
```json
{
  "event_id": "evt_1001",
  "event_type": "project.created",
  "timestamp": "2026-08-19T08:00:00Z",
  "data": {
    "external_project_id": "erp_proj_789",
    "project_name": "Sunrise Heights Phase 1",
    "location": "Kigamboni, Dar es Salaam",
    "city": "Dar es Salaam",
    "country": "Tanzania",
    "total_plots": 150,
    "status": "active"
  }
}
```
**Expected Response:**
```http
HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "success": true,
  "message": "Webhook accepted for processing"
}
```

### 5.2 Plot Synchronization
**Triggered when:** A plot is created, its price changes, or its availability changes in the ERP.
**Endpoint (Platform):** `POST /webhooks/erp/events`

**Payload:**
```json
{
  "event_id": "evt_1002",
  "event_type": "plot.updated",
  "timestamp": "2026-08-19T08:05:00Z",
  "data": {
    "external_project_id": "erp_proj_789",
    "external_plot_id": "erp_plot_101",
    "plot_number": "Block A - 15",
    "area_size_sqm": 400,
    "land_use": "Residential",
    "base_price_tzs": 5000000,
    "availability_status": "available" // available, sold, reserved
  }
}
```
**Expected Response:**
```http
HTTP/1.1 202 Accepted
```

---

## 6. Platform → ERP: Synchronous APIs

Synchronous APIs are used when the Platform requires an immediate response from the ERP to guarantee data consistency.

### 6.1 Reserve Plot (Critical)
**Triggered when:** A customer clicks "Checkout" on the Platform. The Platform MUST lock the plot in the ERP before generating a payment order to prevent race conditions.
**Endpoint (ERP):** `POST /api/v1/plots/reserve`

**Payload:**
```json
{
  "external_project_id": "erp_proj_789",
  "external_plot_id": "erp_plot_101",
  "platform_reference_id": "PLAT-ORD-555",
  "expiry_minutes": 30
}
```

**Success Response (Plot successfully locked):**
```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "success": true,
  "data": {
    "reservation_id": "res_998877",
    "status": "locked_temporarily",
    "expires_at": "2026-08-19T08:35:00Z"
  }
}
```

**Error Response (Plot already sold/booked in ERP):**
```http
HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "success": false,
  "error": {
    "code": "PLOT_UNAVAILABLE",
    "message": "This plot was recently reserved or sold via another channel."
  }
}
```

### 6.2 Release Plot Reservation
**Triggered when:** A customer's checkout session expires without payment, or the order is manually cancelled on the Platform.
**Endpoint (ERP):** `DELETE /api/v1/plots/reserve/{platform_reference_id}`

**Expected Response:**
```http
HTTP/1.1 204 No Content
```

---

## 7. Platform → ERP: Outgoing Webhooks

The Platform pushes data to the ERP when financial transactions or booking milestones occur.

### 7.1 Order Created (Optional)
**Triggered when:** The customer completes the KYC form and an order is officially generated in the Platform's database, pending payment.
**Endpoint (ERP):** `POST /webhooks/platform/events` (Hosted by ERP)

**Payload:**
```json
{
  "event_id": "evt_2001",
  "event_type": "order.created",
  "timestamp": "2026-08-19T08:15:00Z",
  "data": {
    "platform_order_id": "PLAT-ORD-555",
    "external_plot_id": "erp_plot_101",
    "order_type": "installment", // installment, full, cash
    "total_amount_tzs": 5000000,
    "customer": {
      "platform_customer_id": "CUST-999",
      "first_name": "John",
      "last_name": "Doe",
      "phone": "+255700000000",
      "id_number": "NIN-12345"
    }
  }
}
```
**Expected Response:**
```http
HTTP/1.1 202 Accepted
```

### 7.2 Payment Completed (Critical)
**Triggered when:** A payment (Mobile Money/Card) is successfully completed and verified by the Platform. For installments, this fires for *every* individual payment.
**Endpoint (ERP):** `POST /webhooks/platform/events` (Hosted by ERP)

**Payload:**
```json
{
  "event_id": "evt_2002",
  "event_type": "payment.completed",
  "timestamp": "2026-08-19T08:20:00Z",
  "data": {
    "platform_order_id": "PLAT-ORD-555",
    "external_plot_id": "erp_plot_101",
    "transaction_receipt": "TX-MNO-123456",
    "amount_paid_tzs": 500000,
    "balance_remaining_tzs": 4500000,
    "payment_method": "CRDB",
    "is_final_payment": false
  }
}
```
**Expected Response:**
```http
HTTP/1.1 202 Accepted
```

### 7.3 Order Cancelled / Defaulted
**Triggered when:** An order is officially cancelled via the Platform, and the plot is returned to the available pool.
**Endpoint (ERP):** `POST /webhooks/platform/events` (Hosted by ERP)

**Payload:**
```json
{
  "event_id": "evt_2003",
  "event_type": "order.cancelled",
  "timestamp": "2026-08-19T09:00:00Z",
  "data": {
    "platform_order_id": "PLAT-ORD-555",
    "external_plot_id": "erp_plot_101",
    "reason": "Customer requested cancellation."
  }
}
```
**Expected Response:**
```http
HTTP/1.1 202 Accepted
```

---

## 8. Standard Error Responses

When synchronous API calls fail, the system must return a standardized JSON error format.

| HTTP Status | Description | Action Required |
| :--- | :--- | :--- |
| **400 Bad Request** | Missing required fields or invalid data types. | Do not retry. Fix payload. |
| **401 Unauthorized** | Missing or invalid API Key / Signature. | Verify credentials/secrets. |
| **404 Not Found** | External ID referenced does not exist. | Check mapping data. |
| **409 Conflict** | Target resource is in an incompatible state (e.g. plot sold). | Do not retry. Update local state. |
| **422 Unprocessable** | Payload is well-formed but semantically invalid. | Do not retry. Fix payload. |
| **429 Too Many Requests** | Rate limit exceeded. | Retry with exponential backoff. |
| **500 Internal Error** | Unexpected server failure. | Retry with exponential backoff. |

**Example Error Payload:**
```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The field 'external_project_id' is required."
  }
}
```

---

## 9. Retry and Failure Handling Policies

1. **Idempotency Guarantee:** If a webhook is received multiple times with the same `event_id`, the receiver must return `202 Accepted` or `200 OK` on subsequent requests without reprocessing the data.
2. **Webhook Retries:** If a webhook delivery fails (e.g., network timeout, `5xx` error, or `429`), the sender will retry using an exponential backoff schedule (e.g., 1m, 5m, 15m, 1h, 6h).
3. **Dead Letter Queue:** After the maximum number of retries is reached (e.g., 24 hours), the event is marked as permanently failed and requires manual intervention/reconciliation.
