# Use the API with n8n

> Publish to Ur Next Door from n8n: a Header Auth credential for your key, the HTTP Request node for creates and uploads, and a schedule trigger.

Source: https://docs.urnextdoor.com/integrations/n8n/

[n8n](https://n8n.io) is a workflow automation tool you can run yourself or in the cloud. Its **HTTP Request** node can call any endpoint of the API.

## Step 1: Create a key for n8n

Create a key named `n8n` with the scopes the workflow needs, usually `content` (plus `media` for images and `read` for lookups). See [Create an API key](https://docs.urnextdoor.com/getting-started/create-api-key.md).

## Step 2: Store the key as a credential

1. In n8n, open **Credentials** and create a new credential of type **Header Auth**.
2. Set **Name** to `Authorization`.
3. Set **Value** to `Bearer ` followed by your key (`Bearer und_sk_...`, with one space after Bearer).
4. Save it as `Ur Next Door API`.

The credential is stored encrypted by n8n and is not shown in the workflow.

## Step 3: Add an HTTP Request node

Configure it like this to create a post:

| Setting | Value |
| --- | --- |
| Method | `POST` |
| URL | `https://urnextdoor.com/api/v1/posts` |
| Authentication | Generic Credential Type, then **Header Auth**, then `Ur Next Door API` |
| Send Headers | On. Add `Idempotency-Key` with a value unique to this item, for example `{{ $execution.id }}` |
| Send Body | On. Body Content Type **JSON**, Specify Body **Using JSON** |

Body:

```json
{
  "title": "{{ $json.title }}",
  "body": "{{ $json.body_html }}",
  "tags": ["weekly"]
}
```

Map `title` and `body_html` from an earlier node. If those values can contain double quotes, build the body in a **Code** node with `JSON.stringify` (or use **Using Fields Below**) so the JSON stays valid.

> **Shortcut:**
>
> The HTTP Request node can import a cURL command. Copy the cURL example below, use **Import cURL** in the node, then swap the Authorization header for the credential from step 2.

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/posts" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "title": "Five easy ways to save water at home",
  "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
  "tags": [
    "water",
    "home"
  ]
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    title: "Five easy ways to save water at home",
    body: "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
    tags: ["water", "home"],
  }),
});
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/posts",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "title": "Five easy ways to save water at home",
        "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
        "tags": ["water", "home"],
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

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

$payload = [
    'title' => 'Five easy ways to save water at home',
    'body' => '<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>',
    'tags' => ['water', 'home'],
];

$ch = curl_init('https://urnextdoor.com/api/v1/posts');
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;
```

Sample response: `201 Created`

```json
{
  "ok": true,
  "id": 41,
  "slug": "five-easy-ways-to-save-water-at-home",
  "url": "/posts/five-easy-ways-to-save-water-at-home",
  "web_url": "https://urnextdoor.com/posts/five-easy-ways-to-save-water-at-home",
  "item": {
    "id": 41,
    "type": "post",
    "slug": "five-easy-ways-to-save-water-at-home"
  },
  "request_id": "9f8e7d6c5b4a3921"
}
```

## Upload an image

1. Get the image into the workflow as binary data (for example from a **Read Binary File**, **HTTP Request** or AI image node). Note the binary property name, usually `data`.
2. Add an HTTP Request node: Method `POST`, URL `https://urnextdoor.com/api/v1/media`, the same credential.
3. Send Body: Body Content Type **Form-Data**. Add a parameter of type **n8n Binary File** named `file` with Input Data Field Name `data`, and a text parameter `context` = `post`.
4. In the next node, use `{{ $json.data.url }}` as `featured_image` in the create body.

## Run it on a schedule

Start the workflow with a **Schedule Trigger** (for example every weekday at 09:00), read the next row from a sheet or database, and publish it. Or keep the trigger simple and send `scheduled_at` so Ur Next Door does the timing; see [Schedule content](https://docs.urnextdoor.com/guides/schedule-content.md).

## Handle errors

- Turn on the node's retry option only for network errors and `5xx` answers, and keep the same `Idempotency-Key` across retries.
- On `429`, wait for the `Retry-After` seconds; on `422`, route the item to a review step instead of retrying.

## Next steps

- [Zapier](https://docs.urnextdoor.com/integrations/zapier.md).
- [Upload images](https://docs.urnextdoor.com/guides/upload-images.md).
- [Errors](https://docs.urnextdoor.com/errors.md).
