Brand Partner API¶
Небольшой, предсказуемый REST API для создания заказов mintbot, опроса их статуса и получения событий жизненного цикла. JSON на входе, JSON на выходе. Авторизация bearer-token. Идемпотентные записи. Подписанные webhooks.
Панель доступа к API Перейти к webhooks
Кратко¶
| Base URL | https://mint.mintbot.ai/api/v1 |
| Auth | `Authorization: Bearer *** |
| Идемпотентность | Idempotency-Key: <uuid> при каждом POST |
| Content type | application/json |
| Rate limit | 120 req / 60 s на партнёра |
| Webhooks | Подписываются HMAC-SHA256, повторяются до 7 раз |
Аутентификация¶
Каждый запрос передаёт партнёрский API-ключ в заголовке Authorization. Создайте или ротируйте ключ в панели.
Authorization: Bearer mo_liv...xxxx
Content-Type: application/json
У ротации нет льготного окна
Ротация ключа атомарно отзывает предыдущий. Заранее подготовьте замену значения в своей конфигурации, прежде чем нажимать Rotate.
Идемпотентность¶
Каждый POST требует заголовок Idempotency-Key. Подойдёт любой UUID для каждого отдельного запроса.
- Мы кэшируем ответ на 24 часа. Повтор с тем же ключом возвращает исходный ответ с
Idempotent-Replay: true. - Повторное использование того же ключа с другим телом возвращает
409 idempotency_key_mismatch.
Заказы¶
Создать заказ¶
POST /orders
Создаёт заказ и возвращает URL Stripe Checkout. После подтверждения оплаты mintbot разворачивает агента, а событие дохода с долей партнёра записывается автоматически.
Запрос
{
"tier": "s1",
"duration_months": 1,
"credit_usd": 10,
"language": "en",
"external_id": "your-side-id",
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/cart",
"webhook_url": "https://your.app/mintbot-webhook"
}
tier- Один из
trial,s1,s2,s4. duration_months- Срок жизни сервера в календарных месяцах. Должно быть
1,3или12. credit_usd- Необязательно. Предоплаченный чат-кредит, включённый в заказ.
language- Необязательно. Влияет на локаль Stripe Checkout и язык приветственного email.
external_id- Необязательно. Возвращается в каждом webhook и ответе заказа — используйте его, чтобы связывать заказы mintbot со строками в собственной системе.
success_url·cancel_url- URL возврата Stripe Checkout.
{ORDER_ID}подставляется на стороне сервера. webhook_url- Необязательно. Переопределение URL webhook уровня партнёра для конкретного заказа.
Ответ — 201 Created
{
"id": 42,
"tier": "s1",
"duration_months": 1,
"credit_usd": 10,
"amount_cents": 1200,
"currency": "usd",
"status": "awaiting_payment",
"checkout_url": "https://checkout.stripe.com/c/pay/cs_test_…",
"panel_url": null,
"expires_at": null,
"language": "en",
"external_id": "your-side-id",
"created_at": "2026-05-16 09:58:00",
"paid_at": null
}
Пример
curl -X POST https://mint.mintbot.ai/api/v1/orders \
-H "Authorization: Bearer *** \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"tier": "s1",
"duration_months": 1,
"credit_usd": 10,
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/cart"
}'
import os, uuid, requests
r = requests.post(
"https://mint.mintbot.ai/api/v1/orders",
headers={
"Authorization": f"Bearer {os.environ['MINTBOT_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"tier": "s1",
"duration_months": 1,
"credit_usd": 10,
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/cart",
},
timeout=10,
)
r.raise_for_status()
order = r.json()
redirect_to = order["checkout_url"]
Получить заказ¶
GET /orders/{id}
Получает один заказ по его mintbot id. Возвращает ту же структуру, что и POST /orders.
curl https://mint.mintbot.ai/api/v1/orders/42 \
-H "Authorization: Bearer ***
Список заказов¶
GET /orders
Список с курсорной пагинацией, сначала самые новые.
Query parameters
status- Необязательно. Фильтр по
awaiting_payment,completed,deployed,deploy_failedилиexpired. cursor- Необязательно. Передайте
next_cursorс предыдущей страницы, чтобы продолжить.
Ответ
{
"items": [ /* OrderResponse … */ ],
"next_cursor": "37"
}
next_cursor равен null, когда страниц больше нет.
Продлить заказ¶
POST /orders/{id}/renew
Продлевает существующего агента ещё на duration_months. Только инфраструктура — новый чат-кредит не включается. Возвращает новый id заказа и свежий URL Stripe Checkout.
Запрос
{
"duration_months": 1,
"external_id": "your-side-id",
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/account"
}
Доход¶
Прочитать доход¶
GET /revenue
Итоги плюс последние 200 событий ledger.
Query parameters
include_paid- Необязательно, по умолчанию
true. Установитеfalse, чтобы видеть только ещё не выплаченные события.
Ответ
{
"currency": "usd",
"gross_cents": 12000,
"partner_cut_cents": 2000,
"mintbot_cut_cents": 10000,
"unpaid_cents": 800,
"events": [
{
"id": 7,
"order_id": 42,
"kind": "order_paid",
"gross_cents": 1200,
"partner_cut_cents": 200,
"mintbot_cut_cents": 1000,
"currency": "usd",
"created_at": "2026-05-16 09:58:00",
"payout_id": null,
"payout_at": null
}
]
}
Профиль партнёра¶
Получить профиль¶
GET /partner
Возвращает ваш профиль партнёра и невыплаченный баланс — удобно, чтобы показывать заработок внутри собственной админ-панели без самостоятельного хранения этих данных.
Ответ
{
"id": 12,
"email": "you@kliendifirma.com",
"pricing_currency": "usd",
"balance_unpaid_cents": 800,
"webhook_url": "https://your.app/mintbot-webhook",
"api_key_prefix": "mo_live_a12b"
}
Webhooks¶
Когда с заказом что-то происходит, мы отправляем POST с подписанным JSON-событием на настроенный вами URL webhook.
Типы событий¶
| Event | Когда срабатывает |
|---|---|
order.created |
Создана сессия Stripe Checkout, ожидается оплата. |
order.paid |
Stripe подтвердил оплату. Событие дохода записано. |
order.cancelled |
Заказ истёк по времени или был явно отменён. |
agent.provisioning_started |
Для этого заказа запущен deploy pipeline. |
agent.ready |
Deploy завершился успешно. Payload содержит panel_url и expires_at. |
agent.failed |
Deploy pipeline завершился ошибкой. Проверьте поле error, чтобы увидеть сломавшийся шаг. |
agent.expired |
TTL агента истёк. Партнёр может продлить через POST /orders/{id}/renew. |
Доставка и повторы¶
- До 7 попыток по экспоненциальному расписанию:
0s, 30s, 2m, 10m, 1h, 6h, 24h. - После последней попытки доставка помечается как
exhaustedи повторы прекращаются. - Ответьте статусом
2xxв течение 10 секунд, чтобы подтвердить получение.
Заголовки запроса¶
Content-Type: application/json
User-Agent: mintbot-webhook/1.0
X-Mintbot-Signature: t=<unix_ts>,v1=<hex_hmac_sha256>
X-Mintbot-Event-Id: evt_42_order.paid_1747371234567
X-Mintbot-Event-Type: order.paid
Пример payload — order.paid¶
{
"id": 42,
"tier": "s1",
"duration_months": 1,
"credit_usd": 10,
"amount_cents": 1200,
"currency": "usd",
"status": "completed",
"external_id": "your-side-id",
"paid_at": "2026-05-16 10:02:14"
}
Проверка подписи¶
Подпись — это HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Всегда проверяйте сырое тело запроса — повторная сериализация разобранного JSON сломает сравнение.
import hmac, hashlib, time
def verify(secret: str, body: bytes, header: str, tolerance: int = 300) -> bool:
try:
ts_part, v1_part = header.split(",", 1)
ts = int(ts_part.split("=", 1)[1])
sig = v1_part.split("=", 1)[1]
except Exception:
return False
if abs(int(time.time()) - ts) > tolerance:
return False
expected = hmac.new(
secret.encode(),
f"{ts}.".encode() + body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, sig)
const crypto = require("crypto");
function verify(secret, rawBody, header, toleranceSec = 300) {
const [tsPart, v1Part] = header.split(",");
const ts = Number(tsPart.split("=")[1]);
const sig = v1Part.split("=")[1];
if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.`)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(sig, "hex"),
);
}
Идемпотентные получатели
Используйте X-Mintbot-Event-Id как ключ дедупликации — повторы используют тот же id, поэтому строковый INSERT … ON CONFLICT DO NOTHING по этому столбцу делает ваш обработчик безопасным.
Ошибки¶
Каждый ответ с ошибкой содержит стабильный code, по которому можно ветвиться программно. Поле message — человекочитаемая подсказка, оно может меняться между релизами. request_id повторяет заголовок ответа X-Request-Id — указывайте его в обращениях в поддержку.
{
"error": {
"code": "invalid_api_key",
"message": "API key is unknown or revoked.",
"request_id": "f0c2d6c4-…"
}
}
| Code | Значение |
|---|---|
unauthenticated |
Отсутствует или некорректно сформирован заголовок Authorization. |
invalid_api_key |
API key неизвестен или был выведен из обращения ротацией. |
rate_limited |
Превышен лимит на партнёра или на IP — см. Retry-After. |
missing_idempotency_key |
Запрос POST без Idempotency-Key. |
idempotency_key_mismatch |
Тот же ключ повторно использован с другим телом запроса. |
validation_error |
Тело запроса не прошло проверку схемы. |
not_found |
Ресурс не существует или не принадлежит вашему партнёру. |
payment_gateway_error |
Stripe отклонил создание сессии Checkout. |
Rate limits¶
- 120 requests / 60 s rolling window на партнёра. Burst и steady-state используют один и тот же bucket.
- 60 requests / 60 s — отдельный bucket на IP для неаутентифицированных запросов и запросов с неудачной аутентификацией, чтобы brute-force bearer не расходовал партнёрскую квоту.
- Лишние запросы возвращают
429 rate_limitedс заголовкомRetry-After(секунды ожидания).
Нужна помощь?¶
Эта документация написана для партнёров, которые реально пользуются API. Если чего-то не хватает, что-то непонятно или устарело, скажите об этом своему агенту mintbot — он передаст обратную связь, и мы обновим страницу.