Partner API endpoints
Complete Partner API request and response examples for keys, groups, requests, and transactions.
Partner API endpoints
Base URL:
https://p-api.model-gate.com
All requests require:
Authorization: Bearer mg_partner_...
Accept: application/json
All monetary and limit values are JSON decimal strings. Do not parse them as binary floating-point numbers.
Common Bank integration rules
All external timestamps are UTC RFC3339. Collection endpoints use limit (1–100) plus an opaque cursor; never parse or manufacture cursor contents. POST, PATCH, and DELETE requests require Idempotency-Key; retry the same operation with the same key after timeouts. Idempotency records are retained for 7 days. Responses include X-Request-ID, are Cache-Control: no-store, and all errors are JSON. Rate-limit responses are HTTP 429 with Retry-After and X-RateLimit-* headers. Unknown body/query fields are rejected.
The machine-readable OpenAPI 3.1 contract is distributed as resources/contracts/partner-api.openapi.yaml.
Create an API key
Request
curl -X POST https://p-api.model-gate.com/api/v1/partner/keys \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
-H "Content-Type: application/json" \
-d '{
"name": "Telegram user 123",
"group_id": "GROUP_PUBLIC_ID",
"rpm_limit": 30,
"concurrency_limit": 2,
"spend_limit": "20.0000000000",
"usage_price_basis": "official_price",
"usage_price_multiplier": "0.900000"
}'
Response — 201
{
"data": {
"public_id": "KEY_PUBLIC_ID",
"group_id": "GROUP_PUBLIC_ID",
"name": "Telegram user 123",
"key_prefix": "mg_live_ab12",
"key": "mg_live_ab12...",
"status": "active",
"rpm_limit": 30,
"concurrency_limit": 2,
"spend_limit": "20.0000000000",
"usage": "0.0000000000",
"total_spent": "0.0000000000",
"usage_price_basis": "official_price",
"usage_price_multiplier": "0.900000",
"effective_usage_price_basis": "official_price",
"effective_usage_price_multiplier": "0.900000"
}
}
The complete key value is returned only after creation or rotation.
List API keys
Request
curl https://p-api.model-gate.com/api/v1/partner/keys \
-H "Authorization: Bearer mg_partner_..."
Response — 200
{
"data": [
{
"public_id": "KEY_PUBLIC_ID",
"group_id": "GROUP_PUBLIC_ID",
"name": "Telegram user 123",
"status": "active",
"spend_limit": "20.0000000000",
"usage": "3.2500000000",
"total_spent": "1.1400000000"
}
]
}
The list is ordered newest first and uses opaque cursor pagination. Pass limit=1..100; when meta.has_more is true, send meta.next_cursor as the next cursor.
Get one API key
Request
curl https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID \
-H "Authorization: Bearer mg_partner_..."
Response — 200
{
"data": {
"public_id": "KEY_PUBLIC_ID",
"group_id": "GROUP_PUBLIC_ID",
"name": "Telegram user 123",
"status": "active",
"rpm_limit": 30,
"concurrency_limit": 2,
"spend_limit": "20.0000000000",
"usage": "3.2500000000",
"total_spent": "1.1400000000",
"effective_usage_price_basis": "official_price",
"effective_usage_price_multiplier": "0.900000"
}
}
Update an API key
PATCH replaces only the supported mutable values. To remove the key from a group, send an empty group_id. To inherit valuation settings, send null for the key-level basis and multiplier.
Request
curl -X PATCH https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated user name",
"group_id": "GROUP_PUBLIC_ID",
"rpm_limit": 60,
"concurrency_limit": 4,
"spend_limit": "40.0000000000",
"usage_price_basis": null,
"usage_price_multiplier": null
}'
Response — 200
{
"data": {
"public_id": "KEY_PUBLIC_ID",
"spend_limit": "40.0000000000",
"usage": "3.2500000000",
"effective_usage_price_basis": "user_price",
"effective_usage_price_multiplier": "1.000000"
}
}
Delete an API key
Request
curl -X DELETE https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Response — 200
{"data":{"deleted":true}}
Freeze and unfreeze a key
Freeze request
curl -X POST https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/freeze \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Freeze response
{"data":{"public_id":"KEY_PUBLIC_ID","status":"frozen"}}
Unfreeze request
curl -X POST https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/unfreeze \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Unfreeze response
{"data":{"public_id":"KEY_PUBLIC_ID","status":"active"}}
Unfreezing requires a verified owner email.
Rotate a key
Request
curl -X POST https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/rotate \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Response — 200
{
"data": {
"public_id": "KEY_PUBLIC_ID",
"status": "active",
"key_prefix": "mg_live_cd34",
"key": "mg_live_cd34..."
}
}
Reset key usage
Request
curl -X POST https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/reset-usage \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Response — 200
{
"data": {
"public_id": "KEY_PUBLIC_ID",
"usage": "0.0000000000",
"total_spent": "1.1400000000"
}
}
Resetting usage does not change lifetime spend or the account balance.
Get key usage
Request
curl https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/usage \
-H "Authorization: Bearer mg_partner_..."
Response — 200
{
"data": {
"key_id": "KEY_PUBLIC_ID",
"usage": "3.2500000000",
"total_spent": "1.1400000000"
}
}
Get spend limit and remaining usage
Request
curl https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/spent-limit \
-H "Authorization: Bearer mg_partner_..."
Response — 200
{
"data": {
"key_id": "KEY_PUBLIC_ID",
"spend_limit": "20.0000000000",
"usage": "3.2500000000",
"remaining": "16.7500000000"
}
}
When spend_limit is zero, it is unlimited and remaining is null.
Get the latest requests for a key
Detailed request history is a hot-retention dataset controlled by API_REQUESTS_HOT_RETENTION_DAYS (default 7 days). The response metadata reports the active retention window. Use balance transactions for longer-term financial reconciliation.
limit is optional, defaults to 10, and must be an integer from 1 to 100. Finalized requests are ordered by finished_at newest first. When meta.has_more is true, pass meta.next_cursor as the opaque before query parameter to retrieve the next older page. Do not parse or construct cursors yourself.
The response contains immutable user and official per-million-token rates for requests completed after migration 059_request_pricing_audit_snapshot.sql. It also calculates pricing_snapshot.usage_price from the saved basis, multiplier, and historical rates without storing another rate set. This makes the independent usage-limit valuation auditable after catalog prices change. Historical requests created before migration 059 return pricing_snapshot.available: false rather than substituting current prices. Requests executed through Claude/OpenAI-compatible batch APIs are explicitly marked with request_mode: "batch" and include their protocol, batch job ID, custom_id, and the snapshotted Model Gate batch price multiplier.
Latency uses stable Business/Partner names: gateway_overhead_ms, upstream_first_token_ms, and e2e_first_token_ms. The E2E first-token value is measured from Model Gate request start to the first real content token and excludes client-side network/TLS time. The Partner API exposes only the explicit e2e_first_token_ms name; the internal storage column remains api_requests.first_token_ms.
Request
curl "https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/requests?limit=10" \
-H "Authorization: Bearer mg_partner_..."
Response — 200
{
"data": [
{
"request_id": "01KZ...",
"key_id": "KEY_PUBLIC_ID",
"group_id": "GROUP_PUBLIC_ID",
"model": "claude-opus-4.7",
"endpoint": "messages",
"method": "POST",
"status": "succeeded",
"status_code": 200,
"is_stream": false,
"request_mode": "batch",
"batch": {
"protocol": "claude",
"job_id": "msgbatch_01K...",
"custom_id": "request-1",
"price_multiplier": "0.5"
},
"tokens": {
"input": 120,
"output": 45,
"cached": 0,
"cache_write": 0,
"reasoning": 0,
"total": 165
},
"cost": "0.0000345",
"usage_cost": "0.00345",
"official_base_cost": "0.000345",
"currency": "USD",
"usage_pricing": {
"basis": "official_price",
"base_field": "official_base_cost",
"base_amount": "0.000345",
"multiplier": "10",
"usage_cost": "0.00345",
"formula": "round(base_amount * multiplier, 10)"
},
"pricing_snapshot": {
"available": true,
"unit": "per_1m_tokens",
"user_price": {
"input": "0.2",
"output": "1",
"cache_read": "0.02",
"cache_write": "0.25",
"reasoning": "1"
},
"official_price": {
"input": "1",
"output": "5",
"cache_read": "0.1",
"cache_write": "1.25",
"reasoning": "5"
},
"usage_price": {
"input": "10",
"output": "50",
"cache_read": "1",
"cache_write": "12.5",
"reasoning": "50"
}
},
"duration_ms": 842,
"gateway_overhead_ms": 34,
"upstream_first_token_ms": 156,
"e2e_first_token_ms": 190,
"settlement_status": "settled",
"started_at": "2026-08-05T10:00:00Z",
"finished_at": "2026-08-05T10:00:00.842Z"
}
],
"meta": {
"key_id": "KEY_PUBLIC_ID",
"limit": 10,
"returned": 1,
"order": "finished_at_desc",
"next_cursor": "eyJ0IjoiMjAyNi0wOC0wNSAxMDowMDowMCIsInAiOiIwMUt...",
"has_more": true
}
}
To continue with the next older page:
curl "https://p-api.model-gate.com/api/v1/partner/keys/KEY_PUBLIC_ID/requests?limit=10&before=NEXT_CURSOR" \
-H "Authorization: Bearer mg_partner_..."
An invalid before cursor returns HTTP 422. The cursor contains only the finish-time position and external request public ID; internal numeric request IDs are never exposed.
For synchronous and native async requests, request_mode is sync or async and the batch object is omitted. The saved token-rate snapshot allows the historical base calculation to be reproduced as sum(tokens × snapshotted_rate / 1,000,000). Settlement rounds the selected base amount to 10 decimal places and then calculates round(base_amount × multiplier, 10). The stored cost, official_base_cost, and usage_cost fields remain authoritative.
Raw request and response bodies are never returned by this endpoint.
List balance transactions
Request
curl https://p-api.model-gate.com/api/v1/partner/transactions \
-H "Authorization: Bearer mg_partner_..."
Response — 200
{
"data": [
{
"transaction_id": "TRANSACTION_PUBLIC_ID",
"type": "debit",
"source": "api_usage_minute",
"billing_minute_num": 29769120,
"amount": "-0.0232000000",
"request_count": 100,
"balance_before": null,
"balance_after": null,
"api_key_id": null,
"created_at": "2026-08-05T10:00:00Z"
}
]
}
Billable inference is represented as one wallet-ledger row per billing owner and finish-time minute. transaction_id is the durable public ledger identifier. request_count is the number of requests included in that minute debit; billing_minute_num is floor(unix(finished_at)/60). Raw internal ledger id / source_id values are not returned. Aggregate API-usage rows intentionally return null for balance_before, balance_after, and api_key_id; exact key/group/request detail remains available from request history and the panel minute drill-down.
Get current balance
curl https://p-api.model-gate.com/api/v1/partner/balance \
-H "Authorization: Bearer mg_partner_..."
{"data":{"balance":"1234.5678900000","currency":"USD","as_of":"2026-08-28T07:00:00Z"}}
Use this endpoint after account.balance_low callbacks to reconcile the current account wallet without requiring a Model API credential.
List Partner audit events
curl "https://p-api.model-gate.com/api/v1/partner/audit-events?limit=100" \
-H "Authorization: Bearer mg_partner_..."
Audit events record successful Partner management mutations with request ID, action, target, source IP, status, safe metadata, and UTC timestamp. Secrets, bearer tokens, API-key plaintext, idempotency keys, request fingerprints, and replay bodies are not stored in audit metadata. Use cursor for subsequent pages and optional action filtering.
Create a group
Request
curl -X POST https://p-api.model-gate.com/api/v1/partner/groups \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
-H "Content-Type: application/json" \
-d '{
"name": "Telegram bot A",
"description": "Keys created by bot A",
"rpm_limit": 100,
"concurrency_limit": 20,
"spend_limit": "1000.0000000000",
"usage_price_basis": "official_price",
"usage_price_multiplier": "0.900000"
}'
Response — 201
{
"data": {
"public_id": "GROUP_PUBLIC_ID",
"name": "Telegram bot A",
"status": "active",
"spend_limit": "1000.0000000000",
"usage": "0.0000000000",
"total_spent": "0.0000000000",
"usage_price_basis": "official_price",
"usage_price_multiplier": "0.900000"
}
}
List groups
Request
curl https://p-api.model-gate.com/api/v1/partner/groups \
-H "Authorization: Bearer mg_partner_..."
Response
{"data":[{"public_id":"GROUP_PUBLIC_ID","name":"Telegram bot A","status":"active","usage":"0.0000000000"}]}
Get or update a group
Get request
curl https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID \
-H "Authorization: Bearer mg_partner_..."
Get response
{"data":{"public_id":"GROUP_PUBLIC_ID","name":"Telegram bot A","spend_limit":"1000.0000000000","usage":"12.0000000000"}}
Update request
curl -X PATCH https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
-H "Content-Type: application/json" \
-d '{"name":"Telegram bot A production","status":"active","spend_limit":"2000.0000000000","usage_price_basis":"user_price","usage_price_multiplier":"1.200000"}'
Update response
{"data":{"public_id":"GROUP_PUBLIC_ID","name":"Telegram bot A production","spend_limit":"2000.0000000000","usage_price_basis":"user_price","usage_price_multiplier":"1.200000"}}
Delete a group
A non-empty group is not deleted. Move or delete all member API keys first; otherwise the API returns HTTP 409 with group_not_empty.
Request
curl -X DELETE https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Response
{"data":{"deleted":true}}
Keys are detached according to the database foreign-key behavior. Verify membership before deletion.
Reset group usage
Request
curl -X POST https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID/reset-usage \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Response
{"data":{"public_id":"GROUP_PUBLIC_ID","usage":"0.0000000000","total_spent":"8.5000000000"}}
List group members
Request
curl https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID/members \
-H "Authorization: Bearer mg_partner_..."
Response
{"data":[{"public_id":"KEY_PUBLIC_ID","name":"Telegram user 123","status":"active","usage":"3.2500000000","total_spent":"1.1400000000"}]}
Add a key to a group
Request
curl -X POST https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID/members \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
-H "Content-Type: application/json" \
-d '{"key_id":"KEY_PUBLIC_ID"}'
Response
The response is the updated group member list:
{"data":[{"public_id":"KEY_PUBLIC_ID","name":"Telegram user 123","status":"active"}]}
Remove a key from a group
Request
curl -X DELETE https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID/members/KEY_PUBLIC_ID \
-H "Authorization: Bearer mg_partner_..." \
-H "Idempotency-Key: operation-unique-001" \
Response
{"data":{"removed":true}}
Get group usage
Request
curl https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID/usage \
-H "Authorization: Bearer mg_partner_..."
Response
{"data":{"group_id":"GROUP_PUBLIC_ID","usage":"12.0000000000","total_spent":"8.5000000000","usage_reset_at":"2026-08-01T00:00:00Z"}}
Get group statistics
from and to accept only UTC RFC3339 timestamps ending in Z (fractional seconds up to 6 digits are allowed). Statistics are computed only from completed-request aggregates keyed by finished_at; the currently open minute is intentionally excluded, so results may lag by up to the minute aggregation cadence.
average_duration_ms is the average full Model Gate request duration (duration_ms) across completed requests in the selected period. It is not E2E first-token latency, upstream first-token latency, or gateway overhead. The Partner API exposes only this explicit metric name.
Request
curl "https://p-api.model-gate.com/api/v1/partner/groups/GROUP_PUBLIC_ID/stats?from=2026-08-01T00:00:00Z&to=2026-08-05T23:59:59Z" \
-H "Authorization: Bearer mg_partner_..."
Response
{
"data": {
"group_id": "GROUP_PUBLIC_ID",
"requests": 120,
"errors": 3,
"input_tokens": 50000,
"output_tokens": 12000,
"cached_tokens": 8000,
"cache_write_tokens": 1200,
"reasoning_tokens": 2000,
"cost": "8.5000000000",
"average_duration_ms": "842.50",
"from": "2026-08-01T00:00:00Z",
"to": "2026-08-05T23:59:59Z"
}
}
Get an asynchronous result
Request
curl https://p-api.model-gate.com/api/v1/requests/01KZ... \
-H "Authorization: Bearer mg_partner_..."
Processing response
{
"data": {
"request_id": "01KZ...",
"status": "processing",
"created_at": "2026-08-05T10:00:00Z",
"started_at": "2026-08-05T10:00:00Z",
"completed_at": null,
"expires_at": "2026-08-06T10:00:00Z"
}
}
Completed response
{
"data": {
"request_id": "01KZ...",
"status": "completed",
"response_status": 200,
"response_headers": {"Content-Type":"application/json"},
"response": {"id":"resp_example","status":"completed"},
"completed_at": "2026-08-05T10:00:05Z",
"expires_at": "2026-08-06T10:00:00Z"
}
}
Common errors
Invalid Partner API key — 401
{"error":{"type":"invalid_token","message":"Invalid Partner API key"}}
Resource not found — 404
{"error":{"type":"not_found","message":"API key not found"}}
Invalid request — 422
{"error":{"type":"invalid_request","message":"limit must be an integer from 1 to 100"}}
Mutating Partner API JSON bodies are limited to 1 MiB.
Idempotency conflict — 409
The same Idempotency-Key was reused for a different request. Generate a new key for a new logical operation.
Rate limit — 429
The response contains error.code = partner_rate_limit_exceeded and Retry-After. Retry only after the indicated delay.
Method not allowed — 405
The Partner host returns a JSON error plus the HTTP Allow header; it never falls back to an HTML error page.
Business credential principal rules
For a Business Partner API token, only the verified organization owner is accepted. Keys created through Partner API use that owner as both billing owner and credential principal. Employee-principal assignment is performed in the web panel. group_id, when supplied, must identify an active group owned by the Business account; invalid values return HTTP 422 and never fall back to an ungrouped key. Request-history responses may include principal_user_id to identify the credential principal independently from billing ownership.