Server-side access
Call reports from your own backend with OAuth client credentials. No API key, no browser, no human in the loop.
Server-side access
Everything here works from a backend service with no human present. If you are wiring reports into your own dealership platform, DMS, or internal tool, this is the page you need.
There are no API keys, and what to use instead
AI Desk does not issue API keys. It issues OAuth client credentials, which is the standard machine-to-machine grant and does the same job: a client id and a client secret your server holds, exchanged for a short-lived token.
If you already hold a MarketCheck API key for the data endpoints, note that it is a different system and does not work here. AI Desk is a separate account with a separate balance.
One thing to rule out early: the REST endpoint the web app uses to run a report authenticates off a browser session cookie, so it cannot be called server to server. The MCP endpoint below is the supported path, and despite the name it is an ordinary JSON over HTTPS POST. No AI client is involved.
Step 1: create an agent
An agent is a machine identity that belongs to your team and spends your team's balance under limits you set.
Go to Agents and create one. You choose:
| Setting | What it does |
|---|---|
| Scopes | Which reports this agent may run. Grant only what it needs |
| Per-call maximum | Refuses any single call priced above this |
| Daily cap | Total spend per day |
| Monthly cap | Total spend per calendar month |
| Webhook URL | Optional. Receives report.completed and wallet.debited events |
Set the caps. An unattended service that loops is the realistic failure mode, and a monthly cap is the cheapest insurance against it.
You are shown a client id and a client secret. The secret is shown once. Store it the way you store any other production credential.
Step 2: get a token
curl -X POST https://ai.marketcheck.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=reports:window-sticker"
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "reports:window-sticker"
}
Credentials may go in the body as shown, or in an HTTP Basic authorization header. Either is accepted.
Three things to build around:
- The token lives 15 minutes (
expires_in: 900). Fetch one per batch of work rather than one per call. - There is no refresh token. That is deliberate: a short life means revoking an agent takes effect quickly. Request a new token when the old one expires.
- Scope is an intersection, never a widening. Ask for a scope the agent was not granted and you simply do not get it. Omit
scopeentirely and you get everything the agent has.
Step 3: run a report
curl -X POST https://ai.marketcheck.com/mcp \
-H "Authorization: Bearer eyJhbGciOi..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "generate_window_sticker",
"arguments": { "vin": "1GNSKBKC5MR123456" }
}
}'
The tool name and its arguments are listed on each report's page in the catalog.
Step 4: read the response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "{ ...report JSON as a string... }" }]
}
}
content[0].text is a JSON string, not an object. Parse it. This is the single detail that costs integrators the most time.
Parsed, it contains:
{
"data": {
"executive_summary": { "headline": "...", "verdict": "...", "key_metrics": [] },
"window_sticker": { "pdf_url": "https://ai.marketcheck.com/api/window-sticker/9f2c...", "format": "PDF (11x17 landscape)" },
"vehicle": { "vin": "..." },
"hosted_report": { "url": "https://ai.marketcheck.com/r/..." }
},
"billing": {
"list_price_usd": 1.99,
"charged_usd": 1.99,
"balance_after_usd": 248.05,
"report": "Window Sticker"
}
}
Every run reports its own cost and the balance left. Log balance_after_usd and alert on it, so a stalled balance surfaces before it stops your service.
Step 5: fetch artifacts
Reports that produce a file return a URL rather than bytes.
curl -H "Accept: application/pdf" "PDF_URL" -o sticker.pdf
Adding ?format=pdf does the same thing. Open the same URL in a browser instead and it renders in a viewer with download and print controls.
Artifact and hosted-report links are unguessable but public. Anyone holding the link can open it. Treat them like share links: fine to email to a customer, not a substitute for access control.
Errors
| Response | Meaning | What to do |
|---|---|---|
invalid_client |
Unknown client id, wrong secret, or the agent is revoked | Check the credential. Re-issue if revoked |
401 with a WWW-Authenticate header |
Token missing, expired or rejected | Fetch a new token |
| Result text says the connector lacks access | The scope was not granted to this agent | Add the scope on the agent |
| Result text names a balance below the price | Not enough balance | Top up, or enable auto top-up |
isError with a failure message |
The run failed | Nothing was charged. Safe to retry |
A failed run is never billed. This matters for retry logic: you can retry without worrying about double charges.
Keeping an unattended service running
Enable auto top-up. A service that stops at a zero balance at 2am is the most common way this integration fails. Set a threshold and an amount so the balance refills itself.
Watch your caps. A daily or monthly cap doing its job looks identical to a broken integration. Alert on cap-refusal responses so you can tell the two apart.
Revocation is immediate. Revoking an agent takes effect on the next call, not when its current token expires, because the agent's status is re-checked on every request.