# Use the API from a ChatGPT custom GPT

> Build a private custom GPT that posts to Ur Next Door: import the OpenAPI URL as an Action, set Bearer API key auth, keep it Only me, and mind the image limits.

Source: https://docs.urnextdoor.com/ai/chatgpt-actions/

A custom GPT can call the Ur Next Door API through **Actions**. ChatGPT reads the API's OpenAPI document, learns every endpoint, and sends your key with each call. You need a ChatGPT plan that lets you create GPTs.

> **Keep the GPT private:**
>
> The API key you enter in a GPT's Action is used for **every** conversation with that GPT, by anyone who can open it. Anyone with access would post as you. Keep the GPT shared with **Only me**, and never publish it to the GPT Store or share its link.

## Step 1: Create a key for the GPT

In **Settings > API keys** on urnextdoor.com, create a key named `ChatGPT GPT` with the scopes `content` and `read` (add `schedule` to manage scheduled items). `media` is of limited use in a GPT; see the image section below. Pick a 30 or 90 day expiry. See [Create an API key](https://docs.urnextdoor.com/getting-started/create-api-key.md).

## Step 2: Create the GPT

1. In ChatGPT, open **GPTs** and choose **Create**.
2. Switch to the **Configure** tab.
3. Give it a name, for example `My Ur Next Door publisher`.
4. In **Instructions**, paste the [system prompt](https://docs.urnextdoor.com/ai/system-prompt.md), and replace its "MY KEY" section with: `The API key is configured in this GPT's Action. Never ask me for it and never mention it.`

## Step 3: Add the Action

1. At the bottom of **Configure**, choose **Create new action**.
2. Choose **Import from URL** and enter:

```text
   https://urnextdoor.com/api/v1/openapi.json
   ```

   ChatGPT loads the schema and lists the operations (`getMe`, `createPost`, `uploadMedia` and the rest). The document is public, so no key is needed to import it.
3. Under **Authentication**, choose **API Key**, paste your key, and set **Auth Type** to **Bearer**. Save.
4. If ChatGPT asks for a privacy policy URL, use `https://urnextdoor.com/privacy`.

## Step 4: Keep it private and save

Choose **Create** (or **Update**) and, when asked who can access it, choose **Only me**.

## Step 5: Test it

In the preview pane, try:

```text
Who am I on Ur Next Door? Use getMe and tell me my plan and the key's scopes.
```

ChatGPT asks you to allow the call the first time. Then try a real post:

```text
Draft a short post about three ways to save water at home, with the tags water and home.
Show me the draft, and publish it with createPost only after I say yes.
```

ChatGPT asks for confirmation before actions that change data, such as creating a post. Read what it is about to send before you allow it. The request it makes is equivalent to:

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

## Images in a GPT

GPT Actions are text only, and ChatGPT caps a single request at about 100,000 characters. A base64 image is about a third bigger than the file, so base64 upload through `uploadMedia` only works for small images, roughly 70 KB or less.

ChatGPT passes files it holds (images you upload or it generates) to Actions as download links in its own `openaiFileIdRefs` format, which the Ur Next Door API does not read today. Importing files that ChatGPT holds is on the roadmap.

What works today:

- Pass an `https://` image URL as `featured_image` or in `gallery`. The image is linked, not copied.
- For generated or large images, use [Claude Code](https://docs.urnextdoor.com/ai/claude-code.md) or [Codex CLI](https://docs.urnextdoor.com/ai/codex-cli.md), which can upload files from your computer.

## Limits to know

- An Action call times out after about 45 seconds, and ChatGPT backs off after repeated `429` or `500` answers.
- Your daily [quotas](https://docs.urnextdoor.com/limits.md) apply to the GPT like any other tool.
- A GPT may not send an `Idempotency-Key` with every call, so if a create seems to fail, check your profile (or `GET /api/v1/scheduled` for scheduled items) before asking it to try again.

## Frequently asked questions

### Can I share my GPT with my team?

Not safely. A GPT Action holds one API key for everyone who uses the GPT, so every teammate would post as you. Each person should build their own private GPT with their own key. Per-user sign in for a shared GPT needs OAuth, which the API does not offer yet.

### Can the GPT upload the image it just generated?

Not today. ChatGPT passes generated files to Actions as download links that the API does not read, and base64 in an Action is limited to about 70 KB. Use an https image URL, or Claude Code or Codex CLI for files.

### The import says the schema is invalid. What now?

Make sure you entered https://urnextdoor.com/api/v1/openapi.json exactly. A copy of the same document is at https://docs.urnextdoor.com/openapi.json; you can also download it and paste it into the schema box.

## Next steps

- [System prompt](https://docs.urnextdoor.com/ai/system-prompt.md) for the GPT's instructions.
- [Keep your API key safe](https://docs.urnextdoor.com/security.md).
- [Prompt recipes](https://docs.urnextdoor.com/ai/prompt-recipes.md).
