Merchant profile and limits (My Merchant)
Merchant self-service profile: deal limits (min/max/daily), kill_switch, webhook URL — via the cabinet JWT
Merchant profile and limits (My Merchant)
A pair of self-service cabinet endpoints: the merchant reads and updates its own profile without admin rights.
Authentication. GET is dual-homed: the cabinet JWT
(Authorization: Bearer <access_token> from a cabinet login) or the
merchant's HMAC pair (X-Merchant-Token / X-Timestamp /
X-Signature, scope — from the signed token). PATCH — cabinet JWT
only (server-to-server profile mutations over HMAC are not supported).
The path is outside the merchant prefix: /api/v1/p2p/my-merchant.
Retrieving the profile
GET /api/v1/p2p/my-merchant
Authorization: Bearer <access_token>Response example (200 OK, live staging response)
{
"merchant": {
"id": "3e52c59c-3013-4efa-a2db-70cef2adc7ab",
"name": "Merchant TC Sandbox",
"email": "[email protected]",
"status": "ACTIVE",
"kill_switch": false,
"balance_usdt": "95982",
"escrow_usdt": "0",
"trc20_address": "TCBYf3t6D2VshYVQLh55GGBDWTZvjQRoHd",
"trc20_usdt_balance": "0",
"trc20_trx_balance": "0",
"project_url": "",
"checkout_brand_name": "",
"success_redirect_url": "",
"fail_redirect_url": "",
"finance_chat_id": "",
"appeal_chat_id": "",
"ip_whitelist": [],
"api_token": "8102965ace9cbdcd88fb0fb857375c9b3d812327e8871a58d25a79a29d0cba7f",
"withdrawal_address": "",
"moderation_status": "APPROVED",
"api_version": "v2",
"webhook_url": "http://mock-merchant-e2e.syncra-stage.svc.cluster.local:18098/webhook",
"created_at": "2026-08-22T17:12:02Z",
"updated_at": "2026-08-23T18:58:54Z",
"kill_switch_reason": ""
},
"auto_withdrawal_mode": "AUTO_UP_TO_LIMIT",
"auto_withdrawal_effective_limit_usdt_cents": "10000000000"
}Key fields
| Field | Type | Description |
|---|---|---|
kill_switch | bool | Emergency block of merchant activity. Toggled by the platform operator (admin endpoints /disable//enable, unavailable to the merchant's HMAC/JWT sessions); the merchant sees the status in the cabinet. |
kill_switch_reason | string | The reason the platform kill switch is on — the cabinet banner shows it. Empty string when kill_switch=false or when the operator did not provide a reason. Read-only for the merchant. |
deal_min_amount | int64 (optional) | The merchant's own minimum per deal (minor units, kopecks). Absent = no minimum. |
deal_max_amount | int64 (optional) | The merchant's own maximum per deal. Absent = no maximum. |
daily_deal_amount_limit | int64 (optional) | Daily (UTC) cap on combined PayIn+PayOut volume. Absent = no cap set. |
api_token | string | The public API token (rotated via api-keys). |
auto_withdrawal_mode | string | Crypto withdrawal policy: AUTO_UP_TO_LIMIT or MANUAL_ONLY (read-only). |
auto_withdrawal_effective_limit_usdt_cents | int64 | The auto-withdrawal trigger threshold in USDT cents (0 = everything manual). |
The limit fields (deal_min_amount / deal_max_amount /
daily_deal_amount_limit) appear in the response only when set
(proto3 optional): once a limit is removed, the field disappears from
the JSON instead of becoming zero.
Updating the profile and limits
PATCH /api/v1/p2p/my-merchant
Authorization: Bearer <access_token>
Content-Type: application/jsonPartial update: pass only the fields you are changing. The mutable fields
are name, webhook_url, withdrawal_address, success_redirect_url,
fail_redirect_url, checkout_brand_name, finance_chat_id,
appeal_chat_id, ip_whitelist (a JSON array as a string) and the three
deal limits. Status, balances, and fees cannot be changed via self-service
(admin-only).
Pointer semantics for limits
Limit values are int64 in minor units (kopecks):
| Passed in PATCH | Effect |
|---|---|
| field omitted | the limit is unchanged |
0 | the limit is removed (NULL = no limit) |
> 0 | the limit is set (for example, "100000" = 1,000.00 RUB) |
The min/max pair stays consistent: you cannot set a maximum below the active minimum.
Request example (set a 1,000.00 minimum and a 100,000.00 daily cap)
{
"deal_min_amount": "100000",
"daily_deal_amount_limit": "10000000"
}Response example (200 OK)
Returns the full merchant object (same structure as in GET); the set
limits are visible in the deal_min_amount / daily_deal_amount_limit
fields.
Deal creation errors on limit violations
Limits are checked on POST /deals/payin and POST /deals/payout (live
staging responses; see Error codes):
400 INVALID_ARGUMENT — minimum/maximum
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "{\"code\":3,\"message\":\"deal amount is below the merchant minimum: 50000 is below the configured minimum 100000\",\"details\":[]}",
"instance": "/api/v1/p2p-engine"
}{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "{\"code\":3,\"message\":\"deal amount is above the merchant maximum: 150000 is above the configured maximum 100000\",\"details\":[]}",
"instance": "/api/v1/p2p-engine"
}400 FAILED_PRECONDITION — daily volume
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "{\"code\":9,\"message\":\"merchant daily deal volume limit exceeded: today's volume 793921 + requested 150000 exceeds the configured daily cap 100000\",\"details\":[]}",
"instance": "/api/v1/p2p-engine"
}The daily volume is computed over UTC from the same UNION set as
turnover (without EXPIRED/SPAM_REJECTED/
FAILED).
Related materials
- Error codes — the
400rule for all "wrong state" classes. - Deal turnover — how the daily volume is computed.
- Callbacks — setting up
webhook_urland webhook signatures.