# Marketplace endpoints

> Browse services, book one, move your bookings along, and review or reply to reviews.

Source: https://docs.urnextdoor.com/reference/marketplace/

All paths are relative to `https://urnextdoor.com/api/v1`. See [API reference](https://docs.urnextdoor.com/reference.md) for the envelope and headers every endpoint shares.

| Endpoint | What it does | Scope | Daily quota |
| --- | --- | --- | --- |
| [`GET /services`](https://docs.urnextdoor.com/reference/marketplace/#listServices) | Browse services | `browse` | not counted |
| [`GET /services/{slug}`](https://docs.urnextdoor.com/reference/marketplace/#getService) | Read a service | `browse` | not counted |
| [`POST /services/{id}/bookings`](https://docs.urnextdoor.com/reference/marketplace/#bookService) | Book a service | `marketplace` | `booking` |
| [`GET /bookings`](https://docs.urnextdoor.com/reference/marketplace/#listBookings) | List your bookings | `marketplace` | not counted |
| [`POST /bookings/{id}/accept`](https://docs.urnextdoor.com/reference/marketplace/#acceptBooking) | Accept a booking | `marketplace` | not counted |
| [`POST /bookings/{id}/decline`](https://docs.urnextdoor.com/reference/marketplace/#declineBooking) | Decline a booking | `marketplace` | not counted |
| [`POST /bookings/{id}/progress`](https://docs.urnextdoor.com/reference/marketplace/#startBooking) | Start work on a booking | `marketplace` | not counted |
| [`POST /bookings/{id}/deliver`](https://docs.urnextdoor.com/reference/marketplace/#deliverBooking) | Deliver a booking | `marketplace` | not counted |
| [`POST /bookings/{id}/complete`](https://docs.urnextdoor.com/reference/marketplace/#completeBooking) | Complete a booking | `marketplace` | not counted |
| [`POST /bookings/{id}/cancel`](https://docs.urnextdoor.com/reference/marketplace/#cancelBooking) | Cancel a booking | `marketplace` | not counted |
| [`POST /services/{id}/reviews`](https://docs.urnextdoor.com/reference/marketplace/#reviewService) | Review a service | `marketplace` | `booking` |
| [`PUT /reviews/{id}`](https://docs.urnextdoor.com/reference/marketplace/#updateReview) | Edit your review | `marketplace` | `edit` |
| [`POST /reviews/{id}/reply`](https://docs.urnextdoor.com/reference/marketplace/#replyToReview) | Reply to a review | `marketplace` | not counted |

## Browse services {#listServices}

`GET https://urnextdoor.com/api/v1/services`

Marketplace services, near your location when your profile has one.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `browse`
- **Daily quota:** not counted
- **operationId:** `listServices`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `category` | query | No | A category slug. |
| `q` | query | No | Words to match. |
| `min` | query | No | Lowest price, in rupees. |
| `max` | query | No | Highest price, in rupees. |
| `sort` | query | No | recent, rating, popular, price_low or price_high. |
| `offset` | query | No | Start of the page. |
| `limit` | query | No | Items per page (default 12). |

### Example

**cURL**

```bash
curl "https://urnextdoor.com/api/v1/services" \
  -H "Authorization: Bearer $UND_API_KEY"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/services", {
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.get(
    "https://urnextdoor.com/api/v1/services",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/services');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | A page of services. |   |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Read a service {#getService}

`GET https://urnextdoor.com/api/v1/services/{slug}`

A service with its packages and reviews, by id or slug.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `browse`
- **Daily quota:** not counted
- **operationId:** `getService`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `slug` | path | Yes | The service slug or id. |
| `reviews_offset` | query | No | Page through reviews only. |
| `reviews_limit` | query | No | 1 to 50. |

### Example

**cURL**

```bash
curl "https://urnextdoor.com/api/v1/services/rooftop-growers" \
  -H "Authorization: Bearer $UND_API_KEY"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/services/rooftop-growers", {
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.get(
    "https://urnextdoor.com/api/v1/services/rooftop-growers",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/services/rooftop-growers');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {item, reviews, my_review, my_booking, can_review}. |   |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Book a service {#bookService}

`POST https://urnextdoor.com/api/v1/services/{id}/bookings`

Request a booking. The provider is notified and accepts or declines.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** counts as `booking` (see [limits](https://docs.urnextdoor.com/limits.md))
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `bookService`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Request body

Send `Content-Type: application/json`.

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/services/41/bookings" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "package_id": 7,
  "quantity": 1,
  "requirements": "A balcony of 3 by 2 metres."
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/services/41/bookings", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    package_id: 7,
    quantity: 1,
    requirements: "A balcony of 3 by 2 metres.",
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/services/41/bookings",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "package_id": 7,
        "quantity": 1,
        "requirements": "A balcony of 3 by 2 metres.",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'package_id' => 7,
    'quantity' => 1,
    'requirements' => 'A balcony of 3 by 2 metres.',
];

$ch = curl_init('https://urnextdoor.com/api/v1/services/41/bookings');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `201 Created` | {id, status, booking}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## List your bookings {#listBookings}

`GET https://urnextdoor.com/api/v1/bookings`

Bookings you received (as a provider) or placed.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **operationId:** `listBookings`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `tab` | query | No | received or placed. |
| `offset` | query | No | Start of the page. |
| `limit` | query | No | Items per page (default 20). |

### Example

**cURL**

```bash
curl "https://urnextdoor.com/api/v1/bookings" \
  -H "Authorization: Bearer $UND_API_KEY"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/bookings", {
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.get(
    "https://urnextdoor.com/api/v1/bookings",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/bookings');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | A page of bookings, with counts. |   |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Accept a booking {#acceptBooking}

`POST https://urnextdoor.com/api/v1/bookings/{id}/accept`

Accept a booking request for your service.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `acceptBooking`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/bookings/41/accept" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/bookings/41/accept", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/bookings/41/accept",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/bookings/41/accept');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {status, booking}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Decline a booking {#declineBooking}

`POST https://urnextdoor.com/api/v1/bookings/{id}/decline`

Decline a booking request, with an optional note.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `declineBooking`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Request body

Send `Content-Type: application/json`.

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/bookings/41/decline" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "note": "Fully booked this month, sorry."
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/bookings/41/decline", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    note: "Fully booked this month, sorry.",
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/bookings/41/decline",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "note": "Fully booked this month, sorry.",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'note' => 'Fully booked this month, sorry.',
];

$ch = curl_init('https://urnextdoor.com/api/v1/bookings/41/decline');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {status, booking}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Start work on a booking {#startBooking}

`POST https://urnextdoor.com/api/v1/bookings/{id}/progress`

Mark an accepted booking in progress.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `startBooking`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/bookings/41/progress" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/bookings/41/progress", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/bookings/41/progress",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/bookings/41/progress');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {status, booking}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Deliver a booking {#deliverBooking}

`POST https://urnextdoor.com/api/v1/bookings/{id}/deliver`

Mark a booking delivered, with an optional note.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `deliverBooking`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Request body

Send `Content-Type: application/json`.

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/bookings/41/deliver" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "note": "The plan is in your messages."
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/bookings/41/deliver", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    note: "The plan is in your messages.",
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/bookings/41/deliver",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "note": "The plan is in your messages.",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'note' => 'The plan is in your messages.',
];

$ch = curl_init('https://urnextdoor.com/api/v1/bookings/41/deliver');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {status, booking}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Complete a booking {#completeBooking}

`POST https://urnextdoor.com/api/v1/bookings/{id}/complete`

Mark a booking completed (the buyer or the provider). The buyer can then review it.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `completeBooking`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/bookings/41/complete" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/bookings/41/complete", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/bookings/41/complete",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/bookings/41/complete');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {status, booking}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Cancel a booking {#cancelBooking}

`POST https://urnextdoor.com/api/v1/bookings/{id}/cancel`

Cancel a booking that is not finished (the buyer or the provider).

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `cancelBooking`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/bookings/41/cancel" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/bookings/41/cancel", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
  },
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/bookings/41/cancel",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$ch = curl_init('https://urnextdoor.com/api/v1/bookings/41/cancel');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {status, booking}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Review a service {#reviewService}

`POST https://urnextdoor.com/api/v1/services/{id}/reviews`

Rate a service after a completed booking, 1 to 5 stars.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** counts as `booking` (see [limits](https://docs.urnextdoor.com/limits.md))
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `reviewService`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Request body

Send `Content-Type: application/json`.

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/services/41/reviews" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "rating": 5,
  "body": "Quick and thoughtful."
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/services/41/reviews", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    rating: 5,
    body: "Quick and thoughtful.",
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/services/41/reviews",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "rating": 5,
        "body": "Quick and thoughtful.",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'rating' => 5,
    'body' => 'Quick and thoughtful.',
];

$ch = curl_init('https://urnextdoor.com/api/v1/services/41/reviews');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `201 Created` | {review, rating_summary}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Edit your review {#updateReview}

`PUT https://urnextdoor.com/api/v1/reviews/{id}`

Change the rating or text of your review.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** counts as `edit` (see [limits](https://docs.urnextdoor.com/limits.md))
- **operationId:** `updateReview`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Request body

Send `Content-Type: application/json`.

### Example

**cURL**

```bash
curl -X PUT "https://urnextdoor.com/api/v1/reviews/41" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "rating": 4
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/reviews/41", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    rating: 4,
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.put(
    "https://urnextdoor.com/api/v1/reviews/41",
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    json={
        "rating": 4,
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'rating' => 4,
];

$ch = curl_init('https://urnextdoor.com/api/v1/reviews/41');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {review, rating_summary}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |

## Reply to a review {#replyToReview}

`POST https://urnextdoor.com/api/v1/reviews/{id}/reply`

Reply to a review of your service. An empty reply removes it.

- **Auth:** your API key (Bearer or X-API-Key)
- **Scope:** `marketplace`
- **Daily quota:** not counted
- **Idempotency-Key:** supported (see [Idempotency](https://docs.urnextdoor.com/idempotency.md))
- **operationId:** `replyToReview`

### Parameters

| Name | In | Required | Description |
| --- | --- | --- | --- |
| `id` | path | Yes | The numeric id of the item. |

### Request body

Send `Content-Type: application/json`.

### Example

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/reviews/41/reply" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "reply": "Thank you, it was a pleasure!"
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/reviews/41/reply", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    reply: "Thank you, it was a pleasure!",
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/reviews/41/reply",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "reply": "Thank you, it was a pleasure!",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'reply' => 'Thank you, it was a pleasure!',
];

$ch = curl_init('https://urnextdoor.com/api/v1/reviews/41/reply');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

echo $status, PHP_EOL, $body, PHP_EOL;
```

### Responses

| Status | Meaning | Codes |
| --- | --- | --- |
| `200 OK` | {review}. |   |
| `400 Bad Request` | The body is not a JSON object, or the Idempotency-Key header is malformed. | `invalid_json`, `invalid_idempotency_key` |
| `401 Unauthorized` | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | `missing_api_key`, `invalid_api_key` |
| `403 Forbidden` | The key lacks the scope, or the member may not do this. | `insufficient_scope`, `forbidden` |
| `404 Not Found` | Unknown route or item, or the item is not yours. | `not_found` |
| `409 Conflict` | Conflicts with the current state, or the same Idempotency-Key is still being processed. | `conflict`, `idempotency_in_progress` |
| `413 Payload Too Large` | The body is over the limit, or the image is over 8 MB. | `payload_too_large` |
| `415 Unsupported Media Type` | Wrong Content-Type, or a file type that is not allowed. | `unsupported_media_type` |
| `421 Misdirected Request` | Called on a community subdomain. Use https://urnextdoor.com. | `wrong_host` |
| `422 Unprocessable Content` | Field errors, or an Idempotency-Key reused with a different body. | `validation_failed`, `idempotency_key_reused` |
| `429 Too Many Requests` | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | `rate_limited`, `quota_exceeded` |
| `500 Internal Server Error` | Our fault. Safe to retry with the same Idempotency-Key. | `server_error` |
| `503 Service Unavailable` | The API is switched off. | `api_disabled` |
