# Inside Ads API — Agent Cookbook

Base URL: `https://app.inside.ad/api` (referred to as $BASE below).

## 1. Authentication

**AI agents (recommended):** authenticate via OAuth 2.0 (Authorization Code + PKCE,
dynamic client registration — no manual key). See `https://app.inside.ad/auth.md` for the flow, then
send `Authorization: Bearer <access_token>`. The `userId` is derived from the token, so
the `userId` query param is not required (examples below use `userId=$UID` for the manual
apiKey path — with a Bearer token you can omit it).

**Manual (humans/scripts):** every request (except this doc and openapi.json) requires the header:

`Authorization: token <apiKey>`

plus `userId` as a query parameter on every request.

**If the user has not provided credentials, ask them for both:**
- `apiKey` — in the app: Account → Settings.
- `userId` — the user's Telegram user ID (visible in Telegram).

It is convenient to set environment variables:

```bash
export BASE="https://app.inside.ad/api"
export TOKEN="<your apiKey>"
export UID="<your userId>"
```

## 2. Verify the key (whoami)

```bash
curl -s -H "Authorization: token $TOKEN" "$BASE/profile/get?userId=$UID"
```

Returns the profile (fields `apiKey`, balance, plan). HTTP 411 means a wrong key or userId.

## 3. Important rules (must read)

- **set-* methods are GET requests with query parameters**, not POST/PUT. Example: `/ads/setTitle?...`.
- **Lists (countries / languages / categories / keywords) are passed as a JSON string**: `targetCountries=["ru","kg"]` (URL-encode it).
- **Budgets and bids are in milli-dollars**: 5000 = $5, 300 = $0.30. The minimum daily budget is $5.
- **Generation is asynchronous**: `start` returns a `jobId`, then poll `check` until status is `DONE`. Generation is billed against your plan limits.
- **Safety**: a new ad is created as a draft. Do not launch ads (`statusUser=active`) without the user's explicit confirmation — it spends money. To submit for review use `statusUser=onModeration`.

## 4. Recipes

### List my ads

```bash
curl -s -H "Authorization: token $TOKEN" "$BASE/ads/getAds?userId=$UID"
```

### Create an ad (ad post for a channel)

The response contains `newAdId` and `campaignId`.

```bash
curl -s -G -H "Authorization: token $TOKEN" "$BASE/ads/newAd" \
  --data-urlencode "userId=$UID" \
  --data-urlencode "adType=adPost" \
  --data-urlencode "sourceType=channel" \
  --data-urlencode "channelId=-1002291056635" \
  --data-urlencode "title=Working name" \
  --data-urlencode "category=humor" \
  --data-urlencode "costType=CPM" \
  --data-urlencode "dailyBudget=10000" \
  --data-urlencode 'targetCountries=["ru"]' \
  --data-urlencode 'targetLanguages=["ru"]'
```

To advertise a link/website: `sourceType=link` + `link=https://...` instead of `channelId`.
`adType=teaser` is a short teaser (headline + image).

### Set the post body manually

```bash
curl -s -G -H "Authorization: token $TOKEN" "$BASE/ads/setAdPost" \
  --data-urlencode "userId=$UID" \
  --data-urlencode "adId=5001" \
  --data-urlencode "body=<b>Headline</b> ad post text"
```

Images are uploaded separately via `POST /ads/uploadImage` (multipart); then pass the returned file name to `setAdPost` as `filename`.
Teaser headline / internal name: `/ads/setTitle?userId=$UID&adId=5001&title=...` (max 64 characters).

### Budget and bid (editing an existing ad)

```bash
curl -s -G -H "Authorization: token $TOKEN" "$BASE/ads/setDailyBudget" \
  --data-urlencode "userId=$UID" --data-urlencode "adId=5001" --data-urlencode "dailyBudget=10000"

curl -s -G -H "Authorization: token $TOKEN" "$BASE/ads/setBid" \
  --data-urlencode "userId=$UID" --data-urlencode "adId=5001" --data-urlencode "bid=300"
```

Targeting of an existing ad: `/ads/setTargetCountries`, `/ads/setTargetLanguages`, `/ads/setTargetCategories`, `/ads/setTargetKeywords` — exact parameters in openapi.json.

### Generate text + image (async)

```bash
# 1) start — returns {"jobId":"...","status":"pending"}
curl -s -X POST -H "Authorization: token $TOKEN" \
  "$BASE/ads/genAdPost/start?channelId=-1002291056635&adId=5001&languageCode=ru"

# 2) poll — repeat until status becomes DONE (or FAILED)
curl -s -H "Authorization: token $TOKEN" \
  "$BASE/ads/genAdPost/check?jobId=<JOB_ID>&userId=$UID"
```

On `DONE`, `result` contains `adPostHtml`, `adPostFilename` and the refreshed `ads` list. To advertise a link, add `url=https://...` to the start request. `languageCode` controls the output language.

### Submit for review / stop

```bash
# submit for review
curl -s -G -H "Authorization: token $TOKEN" "$BASE/ads/setStatusUser" \
  --data-urlencode "userId=$UID" --data-urlencode "adId=5001" --data-urlencode "statusUser=onModeration"

# stop: the same request with statusUser=onHold
```

The statuses allowed for a specific ad are in the `possibleStatusString` field of the ad object.

## 5. Statistics

```bash
# advertiser summary for a period
curl -s -G -H "Authorization: token $TOKEN" "$BASE/stats/getAdvStats" \
  --data-urlencode "userId=$UID" --data-urlencode "startDate=2026-06-01" --data-urlencode "finishDate=2026-06-15"

# breakdown by ad
curl -s -G -H "Authorization: token $TOKEN" "$BASE/stats/getAdvertiserAds" \
  --data-urlencode "userId=$UID" --data-urlencode "startDate=2026-06-01" --data-urlencode "finishDate=2026-06-15"

# spending and plan limits
curl -s -H "Authorization: token $TOKEN" "$BASE/subscriptions/usage?userId=$UID"
```

Publisher earnings: `/stats/getPublisherStats`. Channel dashboard: `/channels/getChannelDashboard?userId=$UID&channelId=...`.

## 6. Full list of methods

OpenAPI (JSON): https://app.inside.ad/api/public/openapi.json — contains all methods, parameters, types and responses.
