# Use the API from Claude Code

> Let Claude Code publish to Ur Next Door: put your key in UND_API_KEY, add a CLAUDE.md note that points at the docs, allow curl, and start with a test prompt.

Source: https://docs.urnextdoor.com/ai/claude-code/

Claude Code is Anthropic's agent for the terminal, IDEs and the Code tab of the Claude desktop app. It can run `curl`, so it can call the Ur Next Door API directly. Your key stays in an environment variable: Claude Code writes `$UND_API_KEY` in its commands and your shell fills it in, so the key itself never enters the conversation.

## Step 1: Create a key for Claude Code

In **Settings > API keys** on urnextdoor.com, create a key named `Claude Code`, with the scopes `content`, `media` and `read` (add `schedule` if it should move or cancel scheduled items), and an expiry of 30 or 90 days. Copy it. See [Create an API key](https://docs.urnextdoor.com/getting-started/create-api-key.md).

## Step 2: Put the key in the environment, then start Claude Code

Claude Code inherits environment variables from the terminal you start it in. Set the variable first:

```bash
# Paste the key when asked; it is not shown or saved in your history.
read -rs UND_API_KEY && export UND_API_KEY

claude
```

If you saved the key in your shell profile (see [Create an API key](https://docs.urnextdoor.com/getting-started/create-api-key.md)), just open a new terminal and run `claude`.

> **Do not paste the key into the chat:**
>
> Anything you type into the conversation is part of the transcript. Keep the key in the environment and let Claude refer to `$UND_API_KEY`.

## Step 3: Add a note to CLAUDE.md

Claude Code reads `CLAUDE.md` in your project folder at the start of every session. Add this section so it knows how to use the API safely:

**CLAUDE.md**

```markdown
## Ur Next Door API

- Before the first call, read https://docs.urnextdoor.com/llms.txt and follow
  https://docs.urnextdoor.com/ai/system-prompt.txt.
- Base URL: https://urnextdoor.com/api/v1. Contract: https://urnextdoor.com/api/v1/openapi.json
- The API key is in $UND_API_KEY. Use it only as -H "Authorization: Bearer $UND_API_KEY".
  Never print, echo or write the key.
- Send -H "Idempotency-Key: $(uuidgen)" on every POST and DELETE.
- Show me a preview and wait for my yes before publishing anything.
- Upload images with POST /media first, then use data.url as featured_image.
- When done, give me the web_url of everything you created.
```

## Step 4: Allow the docs and, if you want, curl

By default Claude Code asks before it fetches a web page or runs a command. To let it read these docs without asking, add a permission rule to `.claude/settings.json` in your project:

**.claude/settings.json**

```json
{
  "permissions": {
    "allow": [
      "WebFetch(domain:docs.urnextdoor.com)",
      "WebFetch(domain:urnextdoor.com)"
    ]
  }
}
```

You can also add `"Bash(curl:*)"` to that list so it runs `curl` without a prompt each time. Only do that if you are comfortable with it publishing without a permission prompt; the CLAUDE.md rule above still asks it to confirm with you first. Leaving curl out means you approve each request, which is a good way to start.

## Step 5: Try it

Start with something that cannot publish:

```text
Check my Ur Next Door API key with GET /me and tell me my plan and the key's scopes.
Do not create anything.
```

Then a real task:

```text
Write a short post for Ur Next Door about three ways to save water at home.
Use HTML paragraphs and a list, add the tags water and home, and show me a preview.
After I say yes, publish it and give me the web_url.
```

Claude Code will run a request like this one, with `$UND_API_KEY` expanded by your shell:

**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"
}
```

More in [Prompt recipes](https://docs.urnextdoor.com/ai/prompt-recipes.md).

## Images

Claude Code works with files on your computer. If you have an image (from an image model, a screenshot or a photo), ask Claude Code to upload it and use it:

```text
Upload ./cover.png to Ur Next Door as base64 JSON, building the body in a file,
then create the post with that image as featured_image.
```

It should follow [Upload images](https://docs.urnextdoor.com/guides/upload-images.md): build the JSON in a file, send it with `--data-binary @file`, then use `data.url`.

## Troubleshooting

| Symptom | Fix |
| --- | --- |
| `401 missing_api_key` | The variable is empty in Claude Code's environment. Quit Claude Code, set `UND_API_KEY` in that terminal, start `claude` again. Check with `echo ${UND_API_KEY:+set}` (prints `set` without showing the key). |
| `401 invalid_api_key` | Mistyped, revoked or expired key. Create a new one. |
| `403 insufficient_scope` | The key lacks a scope Claude needs, often `media` or `read`. |
| `uuidgen: command not found` | On Linux, install `uuid-runtime`, or ask Claude to use `python3 -c "import uuid; print(uuid.uuid4())"`. |
| Claude invents an id | Remind it to use the lookups; the system prompt says so. |

## Next steps

- [System prompt](https://docs.urnextdoor.com/ai/system-prompt.md) for the full rules.
- [Prompt recipes](https://docs.urnextdoor.com/ai/prompt-recipes.md).
- [Codex CLI](https://docs.urnextdoor.com/ai/codex-cli.md) if you also use OpenAI's agent.
