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.
Codex CLI 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.
Step 2: Put the key in the environment
read -rs UND_API_KEY && export UND_API_KEYStart 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
curlagainst 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:
[sandbox_workspace_write]
network_access = trueRestart 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:
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:
## 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
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 -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"
]
}'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());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
$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
{
"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
Last updated