Skip to content

Partner API endpoints

Complete Partner API request and response examples for keys, groups, requests, and transactions.

No current prepared translation is available. The English source is shown without machine translation.

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 saved user and official per-million-token rates when a pricing snapshot is available. pricing_snapshot.usage_price is derived from its saved basis, multiplier and historical rates so the usage-limit valuation remains explainable after prices change. Older requests without a snapshot return pricing_snapshot.available: false; current prices are not substituted. Claude/OpenAI-compatible batch requests are marked with request_mode: "batch" and include protocol, batch job ID, custom_id and the saved 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.

Exact Partner route inventory

This table is the PartnerBearer contract on the Partner API host; it is not an Images polling surface. Public webhook signatures and portal sessions are separate. The PHP Router suppresses GET response bodies for HEAD; only canonical API methods are listed below.

MethodPathController operation
POST/api/v1/partner/keyscreate
GET/api/v1/partner/keysindex
GET/api/v1/partner/keys/{id}show
PATCH/api/v1/partner/keys/{id}update
DELETE/api/v1/partner/keys/{id}delete
POST/api/v1/partner/keys/{id}/freezefreeze
POST/api/v1/partner/keys/{id}/unfreezeunfreeze
POST/api/v1/partner/keys/{id}/rotaterotate
POST/api/v1/partner/keys/{id}/reset-usageresetUsage
GET/api/v1/partner/keys/{id}/usageusage
GET/api/v1/partner/keys/{id}/spent-limitspentLimit
GET/api/v1/partner/keys/{id}/requestsrequests
GET/api/v1/partner/transactionstransactions
GET/api/v1/partner/balancebalance
GET/api/v1/partner/audit-eventsauditEvents
GET/api/v1/partner/groupsgroupsIndex
POST/api/v1/partner/groupsgroupsCreate
GET/api/v1/partner/groups/{id}groupsShow
PATCH/api/v1/partner/groups/{id}groupsUpdate
DELETE/api/v1/partner/groups/{id}groupsDelete
POST/api/v1/partner/groups/{id}/reset-usagegroupsResetUsage
GET/api/v1/partner/groups/{id}/usagegroupsUsage
GET/api/v1/partner/groups/{id}/membersgroupsMembers
GET/api/v1/partner/groups/{id}/statsgroupsStats
POST/api/v1/partner/groups/{id}/membersgroupsAddMember
DELETE/api/v1/partner/groups/{id}/members/{keyId}groupsRemoveMember
GET/api/v1/requests/{requestId}requestResult