# Use the API from Codex CLI

> Let OpenAI Codex CLI publish to Ur Next Door: set UND_API_KEY, turn on network access in the sandbox, add an AGENTS.md note, and test with GET /me.

Source: https://docs.urnextdoor.com/ai/codex-cli/

[Codex CLI](https://github.com/openai/codex) is OpenAI's coding agent for the terminal. Like Claude Code, it can run `curl` with your key from an environment variable. One difference matters: **Codex runs commands in a sandbox with network access turned off by default**, so a call to the API fails until you allow it.

## Step 1: Create a key for Codex

In **Settings > API keys**, create a key named `Codex CLI` with the scopes `content`, `media` and `read` (plus `schedule` if needed). See [Create an API key](https://docs.urnextdoor.com/getting-started/create-api-key.md).

## Step 2: Put the key in the environment

```bash
read -rs UND_API_KEY && export UND_API_KEY
```

Start Codex from the same terminal so it inherits the variable. Never paste the key into the Codex chat.

## Step 3: Allow network access

Codex's default `workspace-write` sandbox blocks network access. Choose one:

- **Approve each request.** When Codex wants to run `curl` against the API, it asks to run the command outside the sandbox. Approve it. This is the most careful option.
- **Turn network on for the sandbox.** Add this to `~/.codex/config.toml`:

**~/.codex/config.toml**

```toml
[sandbox_workspace_write]
network_access = true
```

Restart Codex after changing the file. Without one of these, `curl` fails with a connection or name resolution error and never reaches Ur Next Door.

## Step 4: Check that Codex can see the key

Ask Codex to run this. It prints `set` without revealing the key:

```bash
echo ${UND_API_KEY:+set}
```

If it prints nothing, Codex's commands are not receiving the variable. Codex decides which environment variables its commands see with the `shell_environment_policy` setting in `~/.codex/config.toml`; make sure that policy passes `UND_API_KEY` through (see the Codex configuration docs), then restart Codex.

## Step 5: Add an AGENTS.md note

Codex reads `AGENTS.md` in your project folder. Add:

**AGENTS.md**

```markdown
## Ur Next Door API

- Read https://docs.urnextdoor.com/llms.txt before the first call 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 key is in $UND_API_KEY. Use it only as -H "Authorization: Bearer $UND_API_KEY". Never print it.
- Network calls need sandbox network access; ask me to approve if it is off.
- Send an Idempotency-Key (a new UUID) on every POST and DELETE.
- Show me a preview and wait for my yes before publishing.
- Finish with the web_url of everything you created.
```

## Step 6: Try it

```text
Call GET https://urnextdoor.com/api/v1/me with my Ur Next Door key from $UND_API_KEY
and tell me my plan and the key's scopes. Do not create anything.
```

Then ask it to draft and publish something. A create looks like this:

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

## Troubleshooting

| Symptom | Fix |
| --- | --- |
| `Could not resolve host` or `Failed to connect` | Network is off in the sandbox. Approve the command or set `network_access = true`. |
| `401 missing_api_key` | Codex's commands do not see `UND_API_KEY`. See step 4. |
| `403 insufficient_scope` | Create a key with the missing scope. |
| Codex prints the key | Stop the session, revoke the key, create a new one, and keep the AGENTS.md rule that forbids printing it. |

## Next steps

- [System prompt](https://docs.urnextdoor.com/ai/system-prompt.md).
- [Prompt recipes](https://docs.urnextdoor.com/ai/prompt-recipes.md).
- [Upload images](https://docs.urnextdoor.com/guides/upload-images.md).
