> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Moderate agent template submissions

> Review tenant-submitted agent templates in the moderation queue and approve them into the public marketplace or reject them with a note the author can act on.

# Moderate agent template submissions

Tenants can submit their own agent templates to the public marketplace (see [Browse, install, and publish marketplace templates](/guides/marketplace-browse-install-publish)). Those submissions do not go live on their own — they land in the **moderation queue**, a platform-owner-gated pending list at **Agents → Marketplace → Moderation**. A platform owner reviews each pending template and either approves it into the public catalog or rejects it with a note. Until an owner decides, the submission is invisible to the public marketplace.

This guide covers the owner review workflow: who can see the queue, how to review a submission, what happens on each verdict, and the API surface behind the page.

## Roles and visibility

Moderation is a platform-wide concern, not a per-tenant action:

* Only a **platform owner** sees the moderation page and can call the moderation endpoints. Members of the submitting tenant below that level get a quiet "owner-only" notice on the page and a `403` from the API.
* The queue looks across tenants: a submission from tenant A appears in the same queue as one from tenant B, and the submitting tenant's id is shown on each row as `submitted_by_tenant_id`.
* A submission the queue hasn't decided on never leaks to the public marketplace — the public list and detail endpoints answer approved rows only, and return `404` for pending and rejected ones.

## The review loop

Open **Agents → Marketplace → Moderation**. The page lists pending submissions oldest-first, so a long backlog drains from the front. Each pending row shows:

* The template's name, category, slug, and description.
* The submitting tenant id (`submitted_by_tenant_id`, when present) and the submission date.
* The declared model, plus a collapsible viewer for the full `system_prompt` — read the prompt before you publish it to every tenant.

To decide a row:

1. Read the description and the system prompt. Check licensing or version claims in the submission text, and any eval or grounding results the author cites.
2. Click **Approve** to publish, or **Reject** to decline.
3. **Approve** opens a confirmation dialog with an optional note — it records a free-text comment on the row and stamps the decision. On confirm, the template is immediately published to the public catalog and installable by every tenant.
4. **Reject** requires a note — the confirm button stays disabled until you write one. On confirm, the submission stays out of the public marketplace but is kept for audit; the note tells the author what to fix before resubmitting.

To approve with changes (for example, a prompt fix you want applied first), reject with a note describing the required change and let the author resubmit — there is no approve-and-edit-a-copy flow.

## Audit

Every moderation decision is written to the audit log with the deciding user, the template, the decision, and any notes. A rejected submission is never deleted from the queue history; paired with the author's own copy, that keeps the reject-and-resubmit loop fully reviewable after the fact. Treat notes accordingly — they are part of the audit record.

## API walkthrough

The moderation page is a consumer of two owner-gated endpoints under the agents marketplace surface. You need a session or key with the platform-owner role — a tenant-level owner role is not sufficient and gets `403`.

### List the pending queue

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/agents/marketplace/pending" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

The response carries the queue plus the backlog size:

```json theme={null}
{
  "data": {
    "templates": [
      {
        "id": "at_9xk2hd81",
        "slug": "tier-1-support-triage",
        "name": "Tier-1 support triage",
        "category": "support",
        "description": "Answers billing and delivery questions, escalates the rest.",
        "system_prompt": "You are a tier-1 support agent…",
        "model": "claude-sonnet-4-6",
        "status": "pending",
        "submitted_by_tenant_id": "tenant_8fk2d93"
      }
    ],
    "total": 1,
    "status": "pending"
  }
}
```

Query parameters:

| Parameter | Default   | Purpose                                                     |
| --------- | --------- | ----------------------------------------------------------- |
| `limit`   | `50`      | Page size, capped at `100`                                  |
| `offset`  | `0`       | Page deeper into the backlog                                |
| `status`  | `pending` | Which slice to return: `pending`, `approved`, or `rejected` |

Each template row includes the full moderation field set — `moderation_notes`, `reviewed_by_user_id`, and `reviewed_at` — that the tenant-facing list and detail endpoints deliberately omit.

### Approve or reject a submission

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/agents/marketplace/tier-1-support-triage/moderate" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "decision": "approve", "notes": "Reads clean; publishing." }'
```

A rejection with its required note:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/agents/marketplace/tier-1-support-triage/moderate" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "decision": "reject", "notes": "The prompt references an internal tool other tenants cannot call." }'
```

The response confirms the verdict:

```json theme={null}
{ "data": { "slug": "tier-1-support-triage", "status": "approved", "decision": "approve" } }
```

Body contract:

| Field      | Required  | Rule                                                                           |
| ---------- | --------- | ------------------------------------------------------------------------------ |
| `decision` | yes       | `approve` or `reject`                                                          |
| `notes`    | on reject | Free text, up to 2000 characters; required when rejecting, optional on approve |

Status transitions: `approve` flips the row to `approved` (public and installable); `reject` flips it to `rejected` (kept, but out of the public catalog).

### Failure modes

| Status | Meaning                                                                                                                                         |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `403`  | The caller is not a platform owner — moderation is not tenant-scoped.                                                                           |
| `404`  | No template with that slug exists.                                                                                                              |
| `409`  | The template is already `approved` or `rejected` — only a `pending` row can be moderated. This prevents a second verdict overwriting the first. |
| `422`  | A `reject` arrived without `notes`.                                                                                                             |

## Safeguards

* **Double-moderation is blocked.** The endpoint only accepts a `pending` row; a second attempt returns `409` rather than overwriting the recorded verdict.
* **Rejecting does not delete.** A rejected submission stays in the queue history for audit, and the author's private copy of the template remains theirs — rejection only keeps the submission out of the public marketplace.
* **The prompt is in the record.** Approving without reading `system_prompt` is the one way a policy-violating template can slip through — the queue renders the full prompt precisely so the reviewer cannot skip it.
* **Reject notes are mandatory.** The schema refuses a `reject` without `notes`, so an author is never left guessing what to change.

## Frequently asked questions

### I'm a tenant owner. Why do I get a 403 on the moderation endpoints?

Moderation is platform-wide, so the gate checks for a platform owner rather than a tenant-level owner role. A tenant owner's submission rights stop at publishing into the queue; the verdict belongs to the platform reviewer.

### The author fixed the prompt. Do they resubmit, or do I re-review the same row?

They resubmit. A rejection is a recorded verdict, not an open invitation to edit the pending row, so the corrected template comes back as a new pending submission.

### A template was approved and later turns out to violate policy. What now?

Pull it with the takedown endpoint — it flips an `approved` (live) row back to `rejected` and always requires a reason:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/agents/marketplace/tier-1-support-triage/takedown" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "notes": "Prompt leaks an internal tool slug — unpublishing pending fix." }'
```

The public list, detail, and install endpoints filter on `approved`, so the template stops being visible and installable the moment takedown lands. Like moderation, takedown requires a platform owner and is audit-logged.

## See also

* [Browse, install, and publish marketplace templates](/guides/marketplace-browse-install-publish) — the tenant-side flow that submits into this queue
* [Audit log](/guides/audit-log) — where moderation decisions are recorded
* [Rate limits](/guides/rate-limits) — bucket behavior on the moderation endpoints
