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

# Make and remove callouts

> POST /v1/callouts: make a callout on a token in the name of one of your users, with the price and market cap stamped at the call. POST /v1/callouts/remove takes one down.

> [!NOTE]
>
> Callouts are switched on per instance on request. Contact us ([contact@cabalspy.xyz](mailto:contact@cabalspy.xyz)) to enable them. Without them every endpoint answers 403 [feature\_not\_enabled](https://docs.cabalspy.xyz/cabalspy-id/errors.md#feature_not_enabled).

## Make a callout

`POST /v1/callouts`

Makes a [callout](https://docs.cabalspy.xyz/cabalspy-id/callouts.md) on a token in the name of one of your users, from your backend. The token's current price and market cap are stamped on it and tracking starts at once. A `callout.created` event goes out on the [stream](https://docs.cabalspy.xyz/cabalspy-id/callouts-stream.md) and as a [webhook](https://docs.cabalspy.xyz/cabalspy-id/webhooks.md#callout-events).

> [!TIP]
>
> Your instance's [settings](https://docs.cabalspy.xyz/cabalspy-id/callouts-settings.md) decide who can call: everyone or KOLs only, holders only (the default), and a cooldown between two callouts of a user (6 hours by default). A user can call the same token once per 24 hours.

Take the user's id from your own session, never from the browser request: whoever controls it makes callouts in that user's name.

### Body

Send JSON with `Content-Type: application/json`.

**external\_id** `string` *required*

Your id of the user, a [platform user](https://docs.cabalspy.xyz/cabalspy-id/white-label.md). Alternatively pass `csid_id`.

**csid\_id** `string`

The user's CabalSpy ID: a platform user, or a person who signed in to your app with the scope `thesis`.

**chain** `string` *required*

Chain of the token. One of solana, bnb, base, eth, rh.

**mint** `string` *required*

Token address: a Solana mint or a 0x contract address.

**note** `string`

Optional text shown with the callout, up to 280 characters. Whitespace is collapsed, at most one link, no blocked words.

**instance** `string`

Your Instance ID. Only needed when your account has several instances.

**api\_key** `string` *required*

Your API key. Alternatively pass it as an Authorization Bearer header.

### Usage Example

```bash cURL theme={null}
curl -X POST "https://api-id.cabalspy.xyz/v1/callouts" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "user_8412",
    "chain": "solana",
    "mint": "3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
    "note": "Dev still holding, chart is coiling."
  }'
```

```python Python theme={null}
import requests

r = requests.post("https://api-id.cabalspy.xyz/v1/callouts", json={
    "external_id": "user_8412",
    "chain": "solana",
    "mint": "3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
    "note": "Dev still holding, chart is coiling.",
}, headers={"Authorization": "Bearer YOUR_KEY"})
if r.status_code == 429:
    print("cooldown, retry in", r.headers["Retry-After"], "seconds")
else:
    c = r.json()["data"]
    print(c["id"], c["at_call"]["price_usd"], c["at_call"]["market_cap_usd"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/callouts", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    external_id: "user_8412",
    chain: "solana",
    mint: "3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
  }),
});
const body = await res.json();
if (!body.success) console.error(body.error.code, body.error.message);
else console.log(body.data.id, body.data.at_call);
```

### Response

**201** with the callout object, as in [List callouts](https://docs.cabalspy.xyz/cabalspy-id/callouts-list.md#the-callout-object). Right after the call, `now` and `peak` equal the price at the call, and `peak.x` and `score_x` are exactly 1.0.

```json Response theme={null}
{
  "success": true,
  "data": {
    "id": "5b0e7c3a-2d4f-4a6b-9c8d-1e2f3a4b5c6d",
    "chain": "solana",
    "mint": "3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
    "token": { "name": "invisibull", "symbol": "invisibull", "logo": "https://..." },
    "author": {
      "csid_id": "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718",
      "external_id": "user_8412",
      "name": "Maze",
      "username": "MazeCCC",
      "avatar_url": "https://cdn.example.com/avatars/8412.png",
      "kol": false
    },
    "note": "Dev still holding, chart is coiling.",
    "called_at": "2026-11-01T18:02:11Z",
    "window_ends_at": "2026-11-02T18:02:11Z",
    "status": "tracking",
    "at_call": { "price_usd": 0.0000412, "market_cap_usd": 41200.0 },
    "now": { "price_usd": 0.0000412, "market_cap_usd": 41200.0, "x": 1.0, "at": "2026-11-01T18:02:11Z" },
    "peak": { "price_usd": 0.0000412, "market_cap_usd": 41200.0, "x": 1.0, "at": "2026-11-01T18:02:11Z" },
    "score_x": 1.0,
    "source": "mobula",
    "tracking": {
      "active": true,
      "phase": "first_day",
      "next_check_at": null,
      "below_10k_since": null,
      "daily_checks": 0,
      "stopped_at": null,
      "stop_reason": null
    }
  },
  "meta": {
    "request_id": "req_5162c7cf7f87",
    "cached": false,
    "cache_age_seconds": 0,
    "version": "v1",
    "timestamp": "2026-11-01T18:02:11Z"
  }
}
```

### Errors

The checks run in this order, so the first rule a callout breaks is the one you see. Messages are written for end users, you can show them as they are.

| Status | Code | Meaning |
| - | - | - |
| 400 | missing\_parameter | chain, mint or external\_id (or csid\_id) is missing. |
| 400 | invalid\_parameter | Unknown chain, invalid token address, invalid csid\_id, or a note that is too long, has more than one link or a blocked word. `parameter` names it. |
| 401 | missing\_api\_key | API key missing. |
| 403 | invalid\_api\_key | API key invalid or not enabled for CabalSpy ID. |
| 403 | [feature\_not\_enabled](https://docs.cabalspy.xyz/cabalspy-id/errors.md#feature_not_enabled) | Callouts are not switched on for your instance. |
| 404 | platform\_user\_not\_found | No platform user with this external\_id. |
| 403 | consent\_required | A csid\_id that is not a platform user has not granted your app the scope thesis. |
| 403 | plan\_user\_limit | The user is beyond your plan's user limit. |
| 403 | sandbox\_user | Sandbox test users cannot make callouts. |
| 403 | [invalid\_account](https://docs.cabalspy.xyz/cabalspy-id/errors.md#invalid_account) | The user's account is not active. |
| 403 | [kol\_only](https://docs.cabalspy.xyz/cabalspy-id/errors.md#kol_only) | Only users with the KOL badge can call on your platform. |
| 403 | posting\_paused | The user is paused from writing. |
| 403 | [not\_a\_holder](https://docs.cabalspy.xyz/cabalspy-id/errors.md#not_a_holder) | Holders only: the user does not hold the token. |
| 409 | [already\_called](https://docs.cabalspy.xyz/cabalspy-id/errors.md#already_called) | The user called this token less than 24 hours ago. |
| 429 | [callout\_cooldown](https://docs.cabalspy.xyz/cabalspy-id/errors.md#callout_cooldown) | The user's cooldown is running. `Retry-After` says how many seconds are left. |
| 422 | [no\_price](https://docs.cabalspy.xyz/cabalspy-id/errors.md#no_price) | The token has no market price yet. |
| 503 | service\_unavailable | The price could not be read right now. Retry in a moment. |
| 429 | rate\_limit\_exceeded | Your API key's request rate is exceeded. |

```json callout_cooldown theme={null}
{
  "success": false,
  "error": {
    "code": "callout_cooldown",
    "message": "One callout every 360 minutes; next one in 118 minutes.",
    "request_id": "req_5162c7cf7f87",
    "docs": "https://docs.cabalspy.xyz/cabalspy-id/errors#callout_cooldown",
    "retry_after": 7051
  }
}
```

## Remove a callout

`POST /v1/callouts/remove`

Takes a callout down, for moderation. It disappears from every list, statistic, leaderboard and competition standing, and a `callout.removed` event goes out on the [stream](https://docs.cabalspy.xyz/cabalspy-id/callouts-stream.md). There is no webhook for removals. Removing cannot be undone.

A removed callout no longer counts for the 24-hour rule or the cooldown, so the user can call again.

### Body

**id** `string` *required*

The callout id.

**reason** `string`

Your reason, up to 200 characters. Stored, not shown.

### Usage Example

```bash cURL theme={null}
curl -X POST "https://api-id.cabalspy.xyz/v1/callouts/remove" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"id": "5b0e7c3a-2d4f-4a6b-9c8d-1e2f3a4b5c6d", "reason": "Spam"}'
```

```python Python theme={null}
import requests

r = requests.post("https://api-id.cabalspy.xyz/v1/callouts/remove",
                  json={"id": "5b0e7c3a-2d4f-4a6b-9c8d-1e2f3a4b5c6d", "reason": "Spam"},
                  headers={"Authorization": "Bearer YOUR_KEY"})
print(r.json()["data"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/callouts/remove", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ id: "5b0e7c3a-2d4f-4a6b-9c8d-1e2f3a4b5c6d" }),
});
console.log((await res.json()).data);
```

### Response

**id** `string`

The callout id.

**removed** `boolean`

Always true.

```json Response theme={null}
{
  "success": true,
  "data": { "id": "5b0e7c3a-2d4f-4a6b-9c8d-1e2f3a4b5c6d", "removed": true },
  "meta": { "request_id": "req_5162c7cf7f87", "cached": false, "cache_age_seconds": 0, "version": "v1", "timestamp": "2026-11-01T19:10:42Z" }
}
```

### Errors

| Status | Code | Meaning |
| - | - | - |
| 400 | missing\_parameter | id is missing. |
| 400 | invalid\_parameter | id is not a valid id, or reason is longer than 200 characters. |
| 403 | [feature\_not\_enabled](https://docs.cabalspy.xyz/cabalspy-id/errors.md#feature_not_enabled) | Callouts are not switched on for your instance. |
| 404 | [callout\_not\_found](https://docs.cabalspy.xyz/cabalspy-id/errors.md#callout_not_found) | No callout with this id in your instance, or it was already removed. |

See [Errors](https://docs.cabalspy.xyz/cabalspy-id/errors.md) for the full list.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.