# Community Library

> Browse, search, favorite and clone shared characters, locations and objects from the Community Library over REST, and report a listing for moderation.

Source: https://nodaro.ai/docs/developers/api/community

The **Community Library API** lets you browse the shared catalog of characters, locations and objects on your Nodaro instance, and clone a listing into your own library. The catalog is curated: the instance's admins publish listings, and every signed-in user can search, favorite, clone and report them.

The library is a multi-user feature. It exists on Nodaro Cloud and on self-hosted Business edition; on a Community edition instance these routes are not registered and answer `404`. The routes take a bearer token: a personal API token (`ndr_…`), an OAuth app token (`ndr_app_…`) or your session token. See [Authentication](https://nodaro.ai/docs/developers/api/authentication).

## Endpoints

| Method | Path | What it does |
| --- | --- | --- |
| `GET` | `/v1/community/browse` | List public listings, one page at a time. |
| `GET` | `/v1/community/detail/:slug` | Get one listing by its slug. |
| `GET` | `/v1/community/favorites` | The listings you favorited. |
| `POST` | `/v1/community/listings/:id/clone` | Copy a listing into your library. |
| `POST` | `/v1/community/listings/:id/favorite` | Add or remove a favorite. |
| `POST` | `/v1/community/listings/:id/report` | Report a listing for moderation. |

## What a listing holds

Every read route returns listings with these public fields only.

| Field | What it holds |
| --- | --- |
| `id`, `slug` | The listing's id, used by the write routes, and its slug, used by the detail route. |
| `entity_type` | `character`, `location` or `object`. |
| `title`, `description`, `category`, `style`, `tags` | What the admin wrote when publishing. |
| `creator_display_name` | Who published the listing. |
| `preview_media_url`, `preview_images` | The preview picture and the gallery of images. |
| `clone_count`, `favorite_count` | How many times the listing was cloned and favorited. |
| `created_at` | When the listing was published. |

## Browse and search

`GET /v1/community/browse` returns `{ data: Listing[], nextCursor }`. Pass `nextCursor` back as `cursor` for the next page; it is `null` when there are no more results.

| Query parameter | What it does |
| --- | --- |
| `entityType` | `character`, `location` or `object`. |
| `q` | Full-text search across the title, the description and the tags. |
| `category` | Only one category. |
| `sort` | `newest` (the default) or `popular`, the most cloned first. |
| `limit` | Page size, 20 by default and 50 at most. |
| `cursor` | The `nextCursor` of the previous page. |

**curl**

```bash
curl "https://app.nodaro.ai/v1/community/browse?entityType=character&sort=popular&limit=20" \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts

const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

const { data, nextCursor } = await client.community.browse({
entityType: 'character',
sort: 'popular',
limit: 20,
})
```

```json
{
"data": [
{
"id": "e4b2d8f1-6a3c-4e9b-8d7f-1c5a3e9b2d6f",
"entity_type": "character",
"slug": "detective-mara",
"title": "Detective Mara",
"description": "Noir-styled investigator",
"category": "people",
"style": "realistic",
"tags": ["noir", "detective"],
"creator_display_name": "Nodaro Team",
"preview_media_url": "https://cdn.nodaro.ai/community/detective-mara.png",
"clone_count": 128,
"favorite_count": 41,
"created_at": "2026-08-30T14:02:11Z"
}
],
"nextCursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wOC0zMCJ9"
}
```

`GET /v1/community/detail/:slug` returns `{ data: Listing }`, or `404 not_found` when the listing does not exist or was taken down. `GET /v1/community/favorites` returns `{ data: Listing[] }`.

## Clone a listing

`POST /v1/community/listings/:id/clone` copies a listing into your library and returns `{ entityType, id }`: the kind and the id of your new character, location or object. The body is `{ entityType }`, which must match the listing's kind.

- **It is a copy, not a link.** The listing's images and clips are copied into your own storage. Later changes to the original, or the listing being taken down, do not affect your copy.
- **It is yours.** The clone is an ordinary character, location or object. Rename it, edit it, regenerate its assets or delete it.
- **Names do not collide.** When you already have one with the same name, the clone gets a unique name with a copy suffix.
- **It uses your storage.** When your account is over its storage limit, the clone is refused with `413 storage_limit_exceeded`.

An OAuth app token needs the `assets:write` scope to clone. Personal API tokens need no scope.

**curl**

```bash
curl -X POST https://app.nodaro.ai/v1/community/listings/e4b2d8f1-6a3c-4e9b-8d7f-1c5a3e9b2d6f/clone \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "entityType": "character" }'
```

**TypeScript SDK**

```ts
const { id } = await client.community.clone(listingId, 'character')
const mara = await client.characters.get(id)
```

```json
{ "entityType": "character", "id": "5a7c9e1b-3d4f-4a2c-b8e6-9f1d3b5a7c2e" }
```

Use the new id with the [Characters](https://nodaro.ai/docs/developers/api/characters), [Locations](https://nodaro.ai/docs/developers/api/locations) or [Objects](https://nodaro.ai/docs/developers/api/objects) API.

## Favorite a listing

`POST /v1/community/listings/:id/favorite` toggles your favorite and returns `{ favorited }`: `true` after adding it, `false` after removing it.

```ts
const { favorited } = await client.community.favorite(listingId)
const { data: favorites } = await client.community.favorites()
```

## Report a listing

`POST /v1/community/listings/:id/report` flags a listing for the admins to review and returns `{ ok: true }`. The body is `{ reason }`, one of:

| `reason` | Use it when the listing… |
| --- | --- |
| `real_person_no_consent` | Shows a real person who did not consent. |
| `inappropriate` | Contains inappropriate content. |
| `ip_violation` | Infringes someone's intellectual property. |
| `other` | Has any other problem. |

```ts
await client.community.report(listingId, 'real_person_no_consent')
```

A listing can be taken down after a report. Copies that users already cloned stay in their libraries.

## Publishing is curated

The catalog is curated by the instance's admins. Publishing a listing is not part of the public API or the SDK. See [Community Library](https://nodaro.ai/docs/guides/community-library).

## Errors

| Status | Code | Meaning |
| --- | --- | --- |
| `400` | `validation_error` | A field is missing or invalid. |
| `401` | `unauthorized` | The token is missing, invalid or revoked. |
| `403` | `insufficient_scope` | An OAuth app token lacks `assets:write` for a clone. |
| `404` | `not_found` | The listing does not exist or was taken down, or the instance is Community edition. |
| `413` | `storage_limit_exceeded` | Your account is over its storage limit. |

## Frequently asked questions

### Is a cloned listing linked to the original?

No. Cloning copies the listing's images and clips into your own storage. The copy is yours to edit, and it survives the original being changed or taken down.

### Can I publish my own character to the Community Library through the API?

No. The catalog is curated by the instance's admins. The API lets every signed-in user browse, favorite, clone and report listings.

### Why does the Community Library API return 404 on my instance?

The library is a multi-user feature of Business and Cloud editions. A Community edition instance does not register these routes, so every call returns 404.

### What happens when I clone a listing and my storage is full?

The clone is refused with 413 storage_limit_exceeded and nothing is copied. Free up storage and try again.
