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

# Custom data

> Add custom items and actions to the author detail sidebar from your own backend

Custom data lets you enrich the author detail view with information and actions from your own systems. When a moderator opens an author, we call an endpoint you host and render the items it returns directly in the author's sidebar.

Common use cases:

* A button to impersonate the user in your own admin
* A link to the user's billing or account page
* A badge showing the user's account status
* Internal IDs, plan names, or links to your support tools

## How it works

<Steps>
  <Step title="Configure your endpoint">
    Enable the integration and set your endpoint URL in the dashboard.
  </Step>

  <Step title="We request data">
    When a moderator opens an author, we send a `POST` request to your endpoint with the author's external id and email.
  </Step>

  <Step title="You respond with items">
    Your endpoint returns a list of items describing what to display.
  </Step>

  <Step title="We render the sidebar">
    The items appear in the author's detail sidebar.
  </Step>
</Steps>

Responses are cached for a short time per author. If your endpoint is slow, errors, or returns an invalid response, the sidebar simply shows nothing — it never blocks the author page.

## Prerequisites

* **Role**: You need the admin or developer role for your project
* **Endpoint**: An `https://` endpoint that accepts `POST` requests and responds within a few seconds

## Configuration

Navigate to **Configure → Custom data** in your project dashboard:

1. Toggle **Enable integration** on
2. Enter your **Endpoint URL** (must be `https://`)
3. Optionally add **request headers** that should be sent on every request (for example, your own API key)
4. Save

## Authenticating requests

So you can confirm that requests genuinely come from us, every request includes your project's webhook signing secret as an `X-Webhook-Secret` header. Compare this value against your project's webhook secret and reject the request if it doesn't match. You can find or rotate this secret on the [Webhooks page](/actions/webhooks).

Any custom headers you configure are sent alongside the `X-Webhook-Secret` header.

## Request

We send a `POST` request with a JSON body:

```json theme={"theme":"nord"}
{
  "version": 1,
  "project_id": "proj_a1b2c3",
  "author_id": "user_12345",
  "email": "user@example.com"
}
```

<ResponseField name="version" type="number">
  The contract version. Currently always `1`.
</ResponseField>

<ResponseField name="project_id" type="string">
  The id of the project the author belongs to.
</ResponseField>

<ResponseField name="author_id" type="string">
  The author's external id — the same `author_id` you send when [submitting content](/api-reference/moderate/analyze-text).
</ResponseField>

<ResponseField name="email" type="string | null">
  The author's email, if known.
</ResponseField>

## Response

Respond with status `200` and a JSON body containing a `version` and an `items` array:

```json theme={"theme":"nord"}
{
  "version": 1,
  "items": [
    { "type": "section", "label": "Billing" },
    {
      "type": "badge",
      "label": "Status",
      "value": "Active",
      "variant": "success",
      "tooltip": "Subscription is in good standing"
    },
    { "type": "stat", "label": "Lifetime value", "value": "$1,240" },
    {
      "type": "button",
      "label": "View billing",
      "url": "https://dashboard.stripe.com/customers/cus_123",
      "variant": "ghost"
    },
    { "type": "divider" },
    {
      "type": "button",
      "label": "Impersonate user",
      "url": "https://admin.example.com/impersonate/user_12345",
      "variant": "outline"
    }
  ]
}
```

A maximum of 30 items are rendered. Any item that doesn't match one of the supported types below is ignored; if the overall response is invalid, nothing is rendered.

### Item types

<ResponseField name="text" type="object">
  A label and a value.

  * `label` (string, optional)
  * `value` (string, required)
  * `tooltip` (string, optional)
</ResponseField>

<ResponseField name="badge" type="object">
  A colored status pill.

  * `label` (string, optional)
  * `value` (string, required)
  * `variant` (string, optional): one of `default`, `success`, `warning`, `danger`, `info`
  * `tooltip` (string, optional)
</ResponseField>

<ResponseField name="button" type="object">
  A button that opens a link in a new tab. Use it for actions and references alike — choose the `variant` to set its visual weight.

  * `label` (string, required)
  * `url` (string, required): must be an `https://` URL
  * `variant` (string, optional): one of `default`, `outline`, `ghost`. Use `ghost` for a lightweight, link-style appearance and `default` or `outline` for a prominent action.
  * `tooltip` (string, optional)
</ResponseField>

<ResponseField name="stat" type="object">
  A label with an emphasized value, for numbers and key figures.

  * `label` (string, required)
  * `value` (string, required)
  * `tooltip` (string, optional)
</ResponseField>

<ResponseField name="section" type="object">
  A heading that groups the items beneath it.

  * `label` (string, required)
</ResponseField>

<ResponseField name="divider" type="object">
  A horizontal separator. Takes no fields.
</ResponseField>

## Example endpoint

```ts theme={"theme":"nord"}
app.post("/moderation/custom-data", async (req, res) => {
  // Verify the request came from us
  if (req.headers["x-webhook-secret"] !== process.env.MODAPI_SECRET_KEY) {
    return res.status(401).end();
  }

  const { author_id, email } = req.body;
  const user = await getUser(author_id);

  res.json({
    version: 1,
    items: [
      {
        type: "badge",
        label: "Status",
        value: user.isBanned ? "Banned" : "Active",
        variant: user.isBanned ? "danger" : "success",
      },
      {
        type: "button",
        label: "Open in admin",
        url: `https://admin.example.com/users/${author_id}`,
      },
    ],
  });
});
```
