jeCheckDevelopers
Get an API key
Developers/API reference/Mystery shopper

Mystery shopper

Mystery shopper visits without an app: create visits with a personal link for a guest, review submitted questionnaires with automatic fraud flags, accept a visit into a regular audit, manage the guest pool and payouts, and order turnkey checks performed by jeCheck guests.

Operations
GET/mystery/visitsList mystery shopper visits GET/mystery/visits/{id}Retrieve a visit POST/mystery/visitsCreate visits PATCH/mystery/visits/{id}Update a visit POST/mystery/visits/{id}/approveAccept a visit POST/mystery/visits/{id}/returnReturn a visit to the guest POST/mystery/visits/{id}/rejectReject a visit POST/mystery/visits/{id}/cancelCancel a visit POST/mystery/visits/{id}/reopenReopen a visit POST/mystery/visits/{id}/inviteSend the link to the guest POST/mystery/visits/{id}/recheckRe-run fraud checks POST/mystery/visits/{id}/ai-reviewRun the AI review of a visit POST/mystery/visits/{id}/payoutSet the payout of a visit DELETE/mystery/visits/{id}Delete a visit GET/mystery/media/{id}Download a private visit file GET/mystery/locationsGet mystery shopper scores by location GET/mystery/shoppersList guests POST/mystery/shoppersAdd a guest PATCH/mystery/shoppers/{id}Update a guest DELETE/mystery/shoppers/{id}Delete a guest GET/mystery/ordersList turnkey orders GET/mystery/orders/catalogGet turnkey pricing catalog POST/mystery/orders/quoteCalculate a turnkey quote GET/mystery/orders/{uuid}Retrieve a turnkey order POST/mystery/ordersCreate a turnkey order draft PATCH/mystery/orders/{uuid}Update a turnkey order draft POST/mystery/orders/{uuid}/submitSubmit a turnkey order POST/mystery/orders/{uuid}/acceptAccept the offer of a turnkey order POST/mystery/orders/{uuid}/cancelCancel a turnkey order POST/mystery/orders/{uuid}/messagesWrite to the jeCheck manager DELETE/mystery/orders/{uuid}Delete a turnkey order draft

GETList mystery shopper visits

GET /mystery/visits

Visits of the company, newest first. The guest link (guest_url) is returned with mystery.manage; payouts — with mystery.payouts (logged). Turnkey visits (managed) hide the guest.

Token scoperead
Permission mystery.view
Plan featuremystery
MCP toolmystery_visits_list · read-only

Query parameters

page integer
Page number, starting from 1. Default 1. Minimum 1.
per_page integer
Items per page (max 100). Default 25. Value 1–100.
status array<string>
scheduled, opened, in_progress, submitted, returned, approved, rejected, cancelled, expired.
location_ids array<integer>
Only these locations.
checklist_id integer
Only this questionnaire.
shopper_id integer
Only this guest.
wave string
Only this wave. Maximum length 120.
managed boolean
true — only turnkey visits, false — only own visits.
flagged boolean
Only submitted visits with serious fraud flags.
date_from date
Submitted from.
date_to date
Submitted to.
updated_since date-time
Only visits changed after this moment — use for incremental sync.

Responses

StatusDescription
200OK — a page of results with meta
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.view, or the plan has no mystery
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/visits?checklist_id=42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits?checklist_id=42', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data, meta } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/visits",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"checklist_id":42},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/visits', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'checklist_id' => 42,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_list",
        "arguments": {
            "checklist_id": 42
        }
    }
}
Response 200
{
  "data": [
    {
      "id": 2041,
      "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
      "status": "submitted",
      "managed": false,
      "order_uuid": null,
      "location": {
        "id": 12,
        "name": "Abay Ave"
      },
      "checklist": {
        "id": 310,
        "name": "Guest experience"
      },
      "shopper": {
        "id": 55,
        "name": "Madina K."
      },
      "wave": "November",
      "window_from": "2026-11-01T09:00:00+05:00",
      "window_to": "2026-11-07T21:00:00+05:00",
      "score": 87.5,
      "level": "warning",
      "submitted_at": "2026-11-03T13:40:00+05:00",
      "created_at": "2026-10-30T10:00:00+05:00",
      "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
      "audit_uuid": null,
      "payout_status": "pending",
      "payout_amount": 4500,
      "updated_at": "2026-11-03T13:40:00+05:00",
      "url": "https://app.jecheck.io/ui/mystery/visits/2041"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 1,
    "has_more": false
  }
}

GETRetrieve a visit

GET /mystery/visits/{id}

Full questionnaire: answers with per-question results, timing, guest report, geolocation vs the location, fraud flags, AI check, files (receipt, photos, audio — private media via mystery.media.get), history. Turnkey visits are available after jeCheck approves them (409 managed_visit before).

Token scoperead
Permission mystery.view
Plan featuremystery
MCP toolmystery_visits_get · read-only

Path parameters

id integer Required
Visit id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.view, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/visits/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/visits/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_get",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTCreate visits

POST /mystery/visits

Creates one visit per location for a questionnaire (checklist). Each visit gets a personal guest link (guest_url) that works without the app; with shopper_id and send_email the guest receives it by email.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_create

Request body

checklist_id integer Required
Questionnaire (checklist).
location_ids array<integer> Required
Items 1–300.
shopper_id integer | null
Guest from the pool.
wave string | null
Maximum length 120.
brief string | null
Task for the guest. Maximum length 5000.
legend string | null
Cover story. Maximum length 5000.
window_from date-time | null
window_to date-time | null
Deadline (after window_from).
send_email boolean
Email the link to the guest.
settings object
Questionnaire requirements and anti-fraud checks.
require_receipt boolean
require_geo boolean
require_timing boolean
require_staff boolean
require_audio boolean
require_place_photos boolean
review_required boolean
false — accept automatically.
min_minutes integer | null
Value 0–600.
geo_radius integer | null
Value 50–5000.
receipt_limit number | null
Minimum 0.
currency string | null
Maximum length 8.

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "checklist_id": 42,
    "location_ids": [
        12,
        14
    ],
    "shopper_id": 42,
    "wave": "string"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "checklist_id": 42,
      "location_ids": [
          12,
          14
      ],
      "shopper_id": 42,
      "wave": "string"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"checklist_id":42,"location_ids":[12,14],"shopper_id":42,"wave":"string"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'checklist_id' => 42,
        'location_ids' => [
            12,
            14,
        ],
        'shopper_id' => 42,
        'wave' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_create",
        "arguments": {
            "checklist_id": 42,
            "location_ids": [
                12,
                14
            ],
            "shopper_id": 42,
            "wave": "string"
        }
    }
}
Response 201
{
  "data": [
    {
      "id": 2041,
      "location": {
        "id": 12,
        "name": "Abay Ave"
      },
      "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
      "invited": true
    }
  ]
}

PATCHUpdate a visit

PATCH /mystery/visits/{id}

Change the brief, cover story, time window or guest of an open visit. The guest cannot be changed after the visit has started.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_update

Path parameters

id integer Required
Visit id.

Request body

brief string | null
Maximum length 5000.
legend string | null
Maximum length 5000.
window_from date-time | null
window_to date-time | null
shopper_id integer | null

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PATCH 'https://app.jecheck.io/api/ext/v1/mystery/visits/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "brief": "string",
    "legend": "string",
    "window_from": "2026-11-03T09:00:00+05:00",
    "window_to": "2026-11-03T09:00:00+05:00"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "brief": "string",
      "legend": "string",
      "window_from": "2026-11-03T09:00:00+05:00",
      "window_to": "2026-11-03T09:00:00+05:00"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.patch(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"brief":"string","legend":"string","window_from":"2026-11-03T09:00:00+05:00","window_to":"2026-11-03T09:00:00+05:00"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('PATCH', 'mystery/visits/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'brief' => 'string',
        'legend' => 'string',
        'window_from' => '2026-11-03T09:00:00+05:00',
        'window_to' => '2026-11-03T09:00:00+05:00',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_update",
        "arguments": {
            "id": 42,
            "brief": "string",
            "legend": "string",
            "window_from": "2026-11-03T09:00:00+05:00",
            "window_to": "2026-11-03T09:00:00+05:00"
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTAccept a visit

POST /mystery/visits/{id}/approve

Accepts a submitted questionnaire: a regular audit is created from it (reports, mailings, corrective tasks follow as for any audit). Returns the visit with audit_uuid.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_approve

Path parameters

id integer Required
Visit id.

Request body

note string | null
Comment. Maximum length 3000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/approve' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/approve', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/approve",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"note":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/approve', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'note' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_approve",
        "arguments": {
            "id": 42,
            "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTReturn a visit to the guest

POST /mystery/visits/{id}/return

Sends a submitted questionnaire back for corrections with a comment; the guest gets the link again.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_return

Path parameters

id integer Required
Visit id.

Request body

note string Required
Length 1–3000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/return' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/return', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/return",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"note":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/return', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'note' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_return",
        "arguments": {
            "id": 42,
            "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTReject a visit

POST /mystery/visits/{id}/reject

Rejects a submitted questionnaire (e.g. fraud); no payout.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_reject

Path parameters

id integer Required
Visit id.

Request body

note string Required
Length 1–3000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/reject' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/reject', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/reject",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"note":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/reject', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'note' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_reject",
        "arguments": {
            "id": 42,
            "note": "Seal on fridge #2 is torn, temperature rises to +9 °C."
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTCancel a visit

POST /mystery/visits/{id}/cancel

Cancels a visit that has not been submitted; the guest link stops working.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_cancel

Path parameters

id integer Required
Visit id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/cancel' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/cancel', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/cancel",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/cancel', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_cancel",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTReopen a visit

POST /mystery/visits/{id}/reopen

Reopens a cancelled or expired visit with a new deadline.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_reopen

Path parameters

id integer Required
Visit id.

Request body

window_to date-time | null
New deadline (future).

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/reopen' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "window_to": "2026-11-03T09:00:00+05:00"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/reopen', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "window_to": "2026-11-03T09:00:00+05:00"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/reopen",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"window_to":"2026-11-03T09:00:00+05:00"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/reopen', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'window_to' => '2026-11-03T09:00:00+05:00',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_reopen",
        "arguments": {
            "id": 42,
            "window_to": "2026-11-03T09:00:00+05:00"
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTSend the link to the guest

POST /mystery/visits/{id}/invite

Emails the questionnaire link to the guest of the visit (requires a guest with an email).

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_invite

Path parameters

id integer Required
Visit id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/invite' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/invite', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/invite",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/invite', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_invite",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTRe-run fraud checks

POST /mystery/visits/{id}/recheck

Recalculates automatic flags: geolocation, time on site, receipt, photo metadata, duplicates.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_recheck

Path parameters

id integer Required
Visit id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/recheck' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/recheck', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/recheck",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/recheck', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_recheck",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTRun the AI review of a visit

POST /mystery/visits/{id}/ai-review

Transcribes audio and asks the AI to check the questionnaire for consistency (requires the AI feature for the company). The result is in ai_check of the visit.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_ai_review

Path parameters

id integer Required
Visit id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/ai-review' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/ai-review', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/ai-review",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/ai-review', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_ai_review",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

POSTSet the payout of a visit

POST /mystery/visits/{id}/payout

Payout status and amount for the guest (receipt reimbursement and fee). Requires mystery.payouts.

Token scopewrite
Permission mystery.payouts
Plan featuremystery
MCP toolmystery_visits_payout

Path parameters

id integer Required
Visit id.

Request body

status string Required
nonependingpaid
amount number | null
Minimum 0.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.payouts, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/visits/42/payout' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "status": "none",
    "amount": 4.5
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42/payout', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "status": "none",
      "amount": 4.5
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42/payout",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"status":"none","amount":4.5},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/visits/42/payout', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'status' => 'none',
        'amount' => 4.5,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_payout",
        "arguments": {
            "id": 42,
            "status": "none",
            "amount": 4.5
        }
    }
}
Response 200
{
  "data": {
    "id": 2041,
    "uuid": "9e8d7c6b-5a49-4382-9716-05f4e3d2c1b0",
    "status": "submitted",
    "managed": false,
    "order_uuid": null,
    "location": {
      "id": 12,
      "name": "Abay Ave"
    },
    "checklist": {
      "id": 310,
      "name": "Guest experience"
    },
    "shopper": {
      "id": 55,
      "name": "Madina K."
    },
    "wave": "November",
    "window_from": "2026-11-01T09:00:00+05:00",
    "window_to": "2026-11-07T21:00:00+05:00",
    "score": 87.5,
    "level": "warning",
    "submitted_at": "2026-11-03T13:40:00+05:00",
    "created_at": "2026-10-30T10:00:00+05:00",
    "guest_url": "https://app.jecheck.io/mg/Ab12Cd34Ef56Gh78Ij90Kl12",
    "audit_uuid": null,
    "payout_status": "pending",
    "payout_amount": 4500,
    "updated_at": "2026-11-03T13:40:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/visits/2041",
    "address": "12 Abay Ave",
    "brief": "Order a cappuccino and a dessert",
    "legend": "Ask about vegan options",
    "settings": {
      "require_receipt": true,
      "require_geo": true,
      "require_timing": true,
      "require_staff": true,
      "require_audio": false,
      "require_place_photos": false,
      "review_required": true,
      "min_minutes": 10,
      "geo_radius": 300,
      "receipt_limit": 5000,
      "currency": "KZT"
    },
    "shopper_contacts": {
      "email": "[email protected]",
      "phone": "+7 701 000 00 00"
    },
    "answers": {
      "8812": {
        "v": 1,
        "c": "Greeted within 10 seconds"
      }
    },
    "results": {
      "8812": {
        "r": 1,
        "t": "Yes"
      }
    },
    "timing": {
      "arrived": "2026-11-03T13:05:00+05:00",
      "left": "2026-11-03T13:31:00+05:00"
    },
    "report": {
      "staff_name": "Aliya",
      "receipt_amount": 4500,
      "summary": "Friendly service, long wait for dessert"
    },
    "geo_start": {
      "lat": 43.2389,
      "lng": 76.9455,
      "acc": 12
    },
    "geo_end": null,
    "geo_ref": {
      "lat": 43.2391,
      "lng": 76.9452
    },
    "distance_m": 35,
    "duration_sec": 1560,
    "flags": [
      {
        "code": "short_visit",
        "level": "warning",
        "text": "The visit took less than the minimum time"
      }
    ],
    "sections_score": [
      {
        "id": 101,
        "name": "Service",
        "score": 90
      }
    ],
    "ai_check": null,
    "review_note": null,
    "reviewer": null,
    "reviewed_at": null,
    "returned_count": 0,
    "audit": null,
    "invited_at": "2026-10-30T10:01:00+05:00",
    "opened_at": "2026-11-03T12:58:00+05:00",
    "started_at": "2026-11-03T13:02:00+05:00",
    "form": [
      {
        "id": 101,
        "name": "Service",
        "hint": null,
        "questions": [
          {
            "id": 8812,
            "section_id": 101,
            "name": "Were you greeted?",
            "hint": null,
            "type": "yes_no",
            "settings": null,
            "weight": 1,
            "can_skip": false,
            "photo": false
          }
        ]
      }
    ],
    "files": [
      {
        "id": 9001,
        "question_id": null,
        "kind": "receipt",
        "url": "https://files.jecheck.io/mystery/761/receipt.jpg",
        "taken_at": "2026-11-03T13:30:00+05:00",
        "mime": "image/jpeg",
        "name": null,
        "duration": null,
        "size": 182044,
        "transcript": null
      }
    ],
    "events": [
      {
        "type": "submitted",
        "at": "2026-11-03T13:40:00+05:00",
        "user": null,
        "note": null
      }
    ]
  }
}

DELDelete a visit

DELETE /mystery/visits/{id}

An accepted visit has become an audit — delete the audit first.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_visits_delete · destructive

Path parameters

id integer Required
Visit id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/mystery/visits/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/visits/42', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.delete(
    "https://app.jecheck.io/api/ext/v1/mystery/visits/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('DELETE', 'mystery/visits/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_visits_delete",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "deleted": true
  }
}

GETDownload a private visit file

GET /mystery/media/{id}

Audio / video recorded by the guest (REST only, not available as an MCP tool).

Token scoperead
Permission mystery.view
Plan featuremystery
MCP toolNot available over MCP

Path parameters

id integer Required
File id from files of the visit.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.view, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/media/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/media/42', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/media/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/media/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
Response 200
{
  "data": {
    "file": "binary"
  }
}

GETGet mystery shopper scores by location

GET /mystery/locations

Accepted visits per location: count, average and last scores, trend of the last 8 visits.

Token scoperead
Permission mystery.view
Plan featuremystery
MCP toolmystery_locations · read-only

Query parameters

date_from date
Submitted from.
date_to date
Submitted to.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.view, or the plan has no mystery
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/locations?date_from=2026-11-01' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/locations?date_from=2026-11-01', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/locations",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"date_from":"2026-11-01"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/locations', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'date_from' => '2026-11-01',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_locations",
        "arguments": {
            "date_from": "2026-11-01"
        }
    }
}
Response 200
{
  "data": [
    {
      "location": {
        "id": 12,
        "name": "Abay Ave"
      },
      "visits": 6,
      "avg": 84.2,
      "last": 87.5,
      "prev": 80,
      "last_at": "2026-11-03T13:40:00+05:00",
      "trend": [
        78,
        80,
        87.5
      ]
    }
  ]
}

GETList guests

GET /mystery/shoppers

The company's pool of mystery guests with visit statistics. Email and phone — only with mystery.payouts (logged).

Token scoperead
Permission mystery.view
Plan featuremystery
MCP toolmystery_shoppers_list · read-only

Query parameters

active boolean
Only active guests.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.view, or the plan has no mystery
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/shoppers?active=true' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/shoppers?active=true', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/shoppers",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    params={"active":True},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/shoppers', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'query' => [
        'active' => true,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_shoppers_list",
        "arguments": {
            "active": true
        }
    }
}
Response 200
{
  "data": [
    {
      "id": 55,
      "name": "Madina K.",
      "city": "Almaty",
      "email": "[email protected]",
      "phone": "+7 701 000 00 00",
      "has_email": true,
      "note": "Prefers weekends",
      "is_active": true,
      "visits": {
        "total": 12,
        "approved": 10,
        "rejected": 1,
        "active": 1,
        "returned": 2
      },
      "last_visit_at": "2026-11-03T13:40:00+05:00"
    }
  ]
}

POSTAdd a guest

POST /mystery/shoppers
Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_shoppers_create

Request body

name string Required
Length 2–255.
email string | null
Maximum length 255.
phone string | null
Maximum length 64.
city string | null
Maximum length 120.
note string | null
Maximum length 2000.
is_active boolean

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/shoppers' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Replace the broken fridge seal",
    "email": "[email protected]",
    "phone": "+77010000000",
    "city": "string"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/shoppers', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "Replace the broken fridge seal",
      "email": "[email protected]",
      "phone": "+77010000000",
      "city": "string"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/shoppers",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal","email":"[email protected]","phone":"+77010000000","city":"string"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/shoppers', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'email' => '[email protected]',
        'phone' => '+77010000000',
        'city' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_shoppers_create",
        "arguments": {
            "name": "Replace the broken fridge seal",
            "email": "[email protected]",
            "phone": "+77010000000",
            "city": "string"
        }
    }
}
Response 201
{
  "data": {
    "id": 55,
    "name": "Madina K.",
    "city": "Almaty",
    "email": "[email protected]",
    "phone": "+7 701 000 00 00",
    "has_email": true,
    "note": "Prefers weekends",
    "is_active": true,
    "visits": {
      "total": 12,
      "approved": 10,
      "rejected": 1,
      "active": 1,
      "returned": 2
    },
    "last_visit_at": "2026-11-03T13:40:00+05:00"
  }
}

PATCHUpdate a guest

PATCH /mystery/shoppers/{id}

Contacts (email, phone) can be changed only with mystery.payouts.

Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_shoppers_update

Path parameters

id integer Required
Guest id.

Request body

name string
Length 2–255.
email string | null
Maximum length 255.
phone string | null
Maximum length 64.
city string | null
Maximum length 120.
note string | null
Maximum length 2000.
is_active boolean

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PATCH 'https://app.jecheck.io/api/ext/v1/mystery/shoppers/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Replace the broken fridge seal",
    "email": "[email protected]",
    "phone": "+77010000000",
    "city": "string"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/shoppers/42', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "name": "Replace the broken fridge seal",
      "email": "[email protected]",
      "phone": "+77010000000",
      "city": "string"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.patch(
    "https://app.jecheck.io/api/ext/v1/mystery/shoppers/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"name":"Replace the broken fridge seal","email":"[email protected]","phone":"+77010000000","city":"string"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('PATCH', 'mystery/shoppers/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'name' => 'Replace the broken fridge seal',
        'email' => '[email protected]',
        'phone' => '+77010000000',
        'city' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_shoppers_update",
        "arguments": {
            "id": 42,
            "name": "Replace the broken fridge seal",
            "email": "[email protected]",
            "phone": "+77010000000",
            "city": "string"
        }
    }
}
Response 200
{
  "data": {
    "id": 55,
    "name": "Madina K.",
    "city": "Almaty",
    "email": "[email protected]",
    "phone": "+7 701 000 00 00",
    "has_email": true,
    "note": "Prefers weekends",
    "is_active": true,
    "visits": {
      "total": 12,
      "approved": 10,
      "rejected": 1,
      "active": 1,
      "returned": 2
    },
    "last_visit_at": "2026-11-03T13:40:00+05:00"
  }
}

DELDelete a guest

DELETE /mystery/shoppers/{id}
Token scopewrite
Permission mystery.manage
Plan featuremystery
MCP toolmystery_shoppers_delete · destructive

Path parameters

id integer Required
Guest id.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.manage, is read-only, or the plan has no mystery
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/mystery/shoppers/42' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/shoppers/42', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.delete(
    "https://app.jecheck.io/api/ext/v1/mystery/shoppers/42",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('DELETE', 'mystery/shoppers/42', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_shoppers_delete",
        "arguments": {
            "id": 42
        }
    }
}
Response 200
{
  "data": {
    "deleted": true
  }
}

GETList turnkey orders

GET /mystery/orders

Orders for mystery shopper checks performed by jeCheck guests, with progress of their visits. No plan feature is needed — the service is paid per order.

Token scoperead
Permission mystery.orders
MCP toolmystery_orders_list · read-only

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/orders' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/orders",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/orders', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_list",
        "arguments": {}
    }
}
Response 200
{
  "data": [
    {
      "uuid": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "number": "MS-00042",
      "status": "quoted",
      "business_type": "cafe",
      "scenarios": [
        "service",
        "cleanliness"
      ],
      "goal": "Check the new menu launch",
      "checklist_id": 310,
      "needs_questionnaire": false,
      "questionnaire_brief": null,
      "visits_per_location": 2,
      "period_from": "2026-11-10",
      "period_to": "2026-11-30",
      "time_slots": [
        "weekday",
        "evening"
      ],
      "requirements": {
        "audio": false,
        "receipt": true,
        "place_photos": false,
        "staff": true,
        "express": false
      },
      "shopper_profile": {
        "gender": "any",
        "age_from": 20,
        "age_to": 35
      },
      "legend": null,
      "reimbursement": 5000,
      "currency": "KZT",
      "quote": {
        "currency": "KZT",
        "visits": 6,
        "lines": [
          {
            "key": "visit",
            "qty": 6,
            "unit": 9000,
            "amount": 54000
          }
        ],
        "total": 84000,
        "per_visit": 14000
      },
      "offer": null,
      "total": 84000,
      "contact_name": "Daniyar Omarov",
      "contact_phone": "+7 701 000 00 01",
      "comment": null,
      "payment_status": "none",
      "decline_reason": null,
      "locations": [
        {
          "id": 77,
          "department_id": 12,
          "name": "Abay Ave",
          "city": "Almaty",
          "address": "12 Abay Ave",
          "visits": 2,
          "note": null
        }
      ],
      "problems": {},
      "progress": {
        "done": 0,
        "avg": null,
        "planned": 6
      },
      "submitted_at": "2026-11-03T10:00:00+05:00",
      "confirmed_at": null,
      "launched_at": null,
      "completed_at": null,
      "created_at": "2026-11-02T18:00:00+05:00",
      "url": "https://app.jecheck.io/ui/mystery/orders/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
    }
  ]
}

GETGet turnkey pricing catalog

GET /mystery/orders/catalog

Whether the service is available in the company's country, business types, cities and prices used by the quote.

Token scoperead
Permission mystery.orders
MCP toolmystery_orders_catalog · read-only

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/orders/catalog' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/catalog', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/catalog",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/orders/catalog', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_catalog",
        "arguments": {}
    }
}
Response 200
{
  "data": {
    "availability": {
      "available": true,
      "country": "KZ",
      "countries": [
        "KZ"
      ]
    },
    "catalog": {
      "currency": "KZT",
      "cities": [
        "Almaty"
      ],
      "major_cities": [
        "Almaty"
      ],
      "business_types": [
        {
          "key": "cafe",
          "reimbursement": 5000,
          "tier": "standard"
        }
      ]
    }
  }
}

POSTCalculate a turnkey quote

POST /mystery/orders/quote

Price estimate for the given order parameters without saving anything.

Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_quote

Request body

business_type string | null
Business type (affects price and reimbursement).
restaurantcafebarcoffeefastfoodretailbeautyclinichotelother
scenarios array<string>
What the guest checks: dine_in, takeaway, delivery, phone, consultation. Maximum items 5.
goal string | null
Maximum length 3000.
checklist_id integer | null
Questionnaire (checklist) to use.
needs_questionnaire boolean
Ask jeCheck to prepare the questionnaire.
questionnaire_brief string | null
Maximum length 3000.
visits_per_location integer | null
Value 1–50.
period_from date | null
period_to date | null
time_slots array<string>
weekday, weekend, morning, lunch, evening. Maximum items 5.
requirements object
audio boolean
receipt boolean
place_photos boolean
staff boolean
express boolean
shopper_profile object
gender string | null
anyfm
age_from integer | null
Value 16–80.
age_to integer | null
Value 16–80.
note string | null
Maximum length 500.
legend string | null
Maximum length 3000.
reimbursement number | null
Receipt reimbursement per visit. Minimum 0.
locations array<object>
Maximum items 300.
department_id integer Required
Location id.
city string | null
Maximum length 120.
address string | null
Maximum length 500.
visits integer | null
Value 1–50.
note string | null
Maximum length 1000.
contact_name string | null
Maximum length 255.
contact_phone string | null
Maximum length 64.
comment string | null
Maximum length 3000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/orders/quote' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "business_type": "restaurant",
    "scenarios": [
        "example"
    ],
    "goal": "string",
    "checklist_id": 42
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/quote', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "business_type": "restaurant",
      "scenarios": [
          "example"
      ],
      "goal": "string",
      "checklist_id": 42
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/quote",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"business_type":"restaurant","scenarios":["example"],"goal":"string","checklist_id":42},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/orders/quote', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'business_type' => 'restaurant',
        'scenarios' => [
            'example',
        ],
        'goal' => 'string',
        'checklist_id' => 42,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_quote",
        "arguments": {
            "business_type": "restaurant",
            "scenarios": [
                "example"
            ],
            "goal": "string",
            "checklist_id": 42
        }
    }
}
Response 200
{
  "data": {
    "currency": "KZT",
    "visits": 6,
    "lines": [
      {
        "key": "visit",
        "qty": 6,
        "unit": 9000,
        "amount": 54000
      }
    ],
    "total": 84000,
    "per_visit": 14000
  }
}

GETRetrieve a turnkey order

GET /mystery/orders/{uuid}

Order terms, quote and offer, progress, visits (results after jeCheck approves them) and the message history.

Token scoperead
Permission mystery.orders
MCP toolmystery_orders_get · read-only

Path parameters

uuid uuid Required
Order UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl 'https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.get(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('GET', 'mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_get",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "uuid": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "number": "MS-00042",
    "status": "quoted",
    "business_type": "cafe",
    "scenarios": [
      "service",
      "cleanliness"
    ],
    "goal": "Check the new menu launch",
    "checklist_id": 310,
    "needs_questionnaire": false,
    "questionnaire_brief": null,
    "visits_per_location": 2,
    "period_from": "2026-11-10",
    "period_to": "2026-11-30",
    "time_slots": [
      "weekday",
      "evening"
    ],
    "requirements": {
      "audio": false,
      "receipt": true,
      "place_photos": false,
      "staff": true,
      "express": false
    },
    "shopper_profile": {
      "gender": "any",
      "age_from": 20,
      "age_to": 35
    },
    "legend": null,
    "reimbursement": 5000,
    "currency": "KZT",
    "quote": {
      "currency": "KZT",
      "visits": 6,
      "lines": [
        {
          "key": "visit",
          "qty": 6,
          "unit": 9000,
          "amount": 54000
        }
      ],
      "total": 84000,
      "per_visit": 14000
    },
    "offer": null,
    "total": 84000,
    "contact_name": "Daniyar Omarov",
    "contact_phone": "+7 701 000 00 01",
    "comment": null,
    "payment_status": "none",
    "decline_reason": null,
    "locations": [
      {
        "id": 77,
        "department_id": 12,
        "name": "Abay Ave",
        "city": "Almaty",
        "address": "12 Abay Ave",
        "visits": 2,
        "note": null
      }
    ],
    "problems": {},
    "progress": {
      "done": 0,
      "avg": null,
      "planned": 6
    },
    "submitted_at": "2026-11-03T10:00:00+05:00",
    "confirmed_at": null,
    "launched_at": null,
    "completed_at": null,
    "created_at": "2026-11-02T18:00:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/orders/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "visits": [
      {
        "id": 2050,
        "location": {
          "id": 12,
          "name": "Abay Ave"
        },
        "stage": "done",
        "window_to": "2026-11-20T21:00:00+05:00",
        "visited_at": "2026-11-18T19:10:00+05:00",
        "score": 91,
        "audit_uuid": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e"
      }
    ],
    "events": [
      {
        "id": 1,
        "type": "message",
        "side": "service",
        "author": "Anna",
        "body": "We have prepared the terms",
        "payload": null,
        "at": "2026-11-03T11:00:00+05:00"
      }
    ]
  }
}

POSTCreate a turnkey order draft

POST /mystery/orders

Saves a draft with a quote; problems lists what is missing before submit. The service is currently available only in some countries (see the catalog).

Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_create

Request body

business_type string | null
Business type (affects price and reimbursement).
restaurantcafebarcoffeefastfoodretailbeautyclinichotelother
scenarios array<string>
What the guest checks: dine_in, takeaway, delivery, phone, consultation. Maximum items 5.
goal string | null
Maximum length 3000.
checklist_id integer | null
Questionnaire (checklist) to use.
needs_questionnaire boolean
Ask jeCheck to prepare the questionnaire.
questionnaire_brief string | null
Maximum length 3000.
visits_per_location integer | null
Value 1–50.
period_from date | null
period_to date | null
time_slots array<string>
weekday, weekend, morning, lunch, evening. Maximum items 5.
requirements object
audio boolean
receipt boolean
place_photos boolean
staff boolean
express boolean
shopper_profile object
gender string | null
anyfm
age_from integer | null
Value 16–80.
age_to integer | null
Value 16–80.
note string | null
Maximum length 500.
legend string | null
Maximum length 3000.
reimbursement number | null
Receipt reimbursement per visit. Minimum 0.
locations array<object>
Maximum items 300.
department_id integer Required
Location id.
city string | null
Maximum length 120.
address string | null
Maximum length 500.
visits integer | null
Value 1–50.
note string | null
Maximum length 1000.
contact_name string | null
Maximum length 255.
contact_phone string | null
Maximum length 64.
comment string | null
Maximum length 3000.

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/orders' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "business_type": "restaurant",
    "scenarios": [
        "example"
    ],
    "goal": "string",
    "checklist_id": 42
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "business_type": "restaurant",
      "scenarios": [
          "example"
      ],
      "goal": "string",
      "checklist_id": 42
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/orders",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"business_type":"restaurant","scenarios":["example"],"goal":"string","checklist_id":42},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/orders', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'business_type' => 'restaurant',
        'scenarios' => [
            'example',
        ],
        'goal' => 'string',
        'checklist_id' => 42,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_create",
        "arguments": {
            "business_type": "restaurant",
            "scenarios": [
                "example"
            ],
            "goal": "string",
            "checklist_id": 42
        }
    }
}
Response 201
{
  "data": {
    "uuid": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "number": "MS-00042",
    "status": "quoted",
    "business_type": "cafe",
    "scenarios": [
      "service",
      "cleanliness"
    ],
    "goal": "Check the new menu launch",
    "checklist_id": 310,
    "needs_questionnaire": false,
    "questionnaire_brief": null,
    "visits_per_location": 2,
    "period_from": "2026-11-10",
    "period_to": "2026-11-30",
    "time_slots": [
      "weekday",
      "evening"
    ],
    "requirements": {
      "audio": false,
      "receipt": true,
      "place_photos": false,
      "staff": true,
      "express": false
    },
    "shopper_profile": {
      "gender": "any",
      "age_from": 20,
      "age_to": 35
    },
    "legend": null,
    "reimbursement": 5000,
    "currency": "KZT",
    "quote": {
      "currency": "KZT",
      "visits": 6,
      "lines": [
        {
          "key": "visit",
          "qty": 6,
          "unit": 9000,
          "amount": 54000
        }
      ],
      "total": 84000,
      "per_visit": 14000
    },
    "offer": null,
    "total": 84000,
    "contact_name": "Daniyar Omarov",
    "contact_phone": "+7 701 000 00 01",
    "comment": null,
    "payment_status": "none",
    "decline_reason": null,
    "locations": [
      {
        "id": 77,
        "department_id": 12,
        "name": "Abay Ave",
        "city": "Almaty",
        "address": "12 Abay Ave",
        "visits": 2,
        "note": null
      }
    ],
    "problems": {},
    "progress": {
      "done": 0,
      "avg": null,
      "planned": 6
    },
    "submitted_at": "2026-11-03T10:00:00+05:00",
    "confirmed_at": null,
    "launched_at": null,
    "completed_at": null,
    "created_at": "2026-11-02T18:00:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/orders/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
  }
}

PATCHUpdate a turnkey order draft

PATCH /mystery/orders/{uuid}

Only drafts can be edited; after submitting, changes are agreed with the manager in messages.

Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_update

Path parameters

uuid uuid Required
Order UUID.

Request body

business_type string | null
Business type (affects price and reimbursement).
restaurantcafebarcoffeefastfoodretailbeautyclinichotelother
scenarios array<string>
What the guest checks: dine_in, takeaway, delivery, phone, consultation. Maximum items 5.
goal string | null
Maximum length 3000.
checklist_id integer | null
Questionnaire (checklist) to use.
needs_questionnaire boolean
Ask jeCheck to prepare the questionnaire.
questionnaire_brief string | null
Maximum length 3000.
visits_per_location integer | null
Value 1–50.
period_from date | null
period_to date | null
time_slots array<string>
weekday, weekend, morning, lunch, evening. Maximum items 5.
requirements object
audio boolean
receipt boolean
place_photos boolean
staff boolean
express boolean
shopper_profile object
gender string | null
anyfm
age_from integer | null
Value 16–80.
age_to integer | null
Value 16–80.
note string | null
Maximum length 500.
legend string | null
Maximum length 3000.
reimbursement number | null
Receipt reimbursement per visit. Minimum 0.
locations array<object>
Maximum items 300.
department_id integer Required
Location id.
city string | null
Maximum length 120.
address string | null
Maximum length 500.
visits integer | null
Value 1–50.
note string | null
Maximum length 1000.
contact_name string | null
Maximum length 255.
contact_phone string | null
Maximum length 64.
comment string | null
Maximum length 3000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X PATCH 'https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "business_type": "restaurant",
    "scenarios": [
        "example"
    ],
    "goal": "string",
    "checklist_id": 42
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "business_type": "restaurant",
      "scenarios": [
          "example"
      ],
      "goal": "string",
      "checklist_id": 42
  }),
});
const { data } = await res.json();
import os, requests

res = requests.patch(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"business_type":"restaurant","scenarios":["example"],"goal":"string","checklist_id":42},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('PATCH', 'mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'business_type' => 'restaurant',
        'scenarios' => [
            'example',
        ],
        'goal' => 'string',
        'checklist_id' => 42,
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_update",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "business_type": "restaurant",
            "scenarios": [
                "example"
            ],
            "goal": "string",
            "checklist_id": 42
        }
    }
}
Response 200
{
  "data": {
    "uuid": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "number": "MS-00042",
    "status": "quoted",
    "business_type": "cafe",
    "scenarios": [
      "service",
      "cleanliness"
    ],
    "goal": "Check the new menu launch",
    "checklist_id": 310,
    "needs_questionnaire": false,
    "questionnaire_brief": null,
    "visits_per_location": 2,
    "period_from": "2026-11-10",
    "period_to": "2026-11-30",
    "time_slots": [
      "weekday",
      "evening"
    ],
    "requirements": {
      "audio": false,
      "receipt": true,
      "place_photos": false,
      "staff": true,
      "express": false
    },
    "shopper_profile": {
      "gender": "any",
      "age_from": 20,
      "age_to": 35
    },
    "legend": null,
    "reimbursement": 5000,
    "currency": "KZT",
    "quote": {
      "currency": "KZT",
      "visits": 6,
      "lines": [
        {
          "key": "visit",
          "qty": 6,
          "unit": 9000,
          "amount": 54000
        }
      ],
      "total": 84000,
      "per_visit": 14000
    },
    "offer": null,
    "total": 84000,
    "contact_name": "Daniyar Omarov",
    "contact_phone": "+7 701 000 00 01",
    "comment": null,
    "payment_status": "none",
    "decline_reason": null,
    "locations": [
      {
        "id": 77,
        "department_id": 12,
        "name": "Abay Ave",
        "city": "Almaty",
        "address": "12 Abay Ave",
        "visits": 2,
        "note": null
      }
    ],
    "problems": {},
    "progress": {
      "done": 0,
      "avg": null,
      "planned": 6
    },
    "submitted_at": "2026-11-03T10:00:00+05:00",
    "confirmed_at": null,
    "launched_at": null,
    "completed_at": null,
    "created_at": "2026-11-02T18:00:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/orders/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
  }
}

POSTSubmit a turnkey order

POST /mystery/orders/{uuid}/submit

Sends the completed draft to jeCheck managers, who reply with an offer.

Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_submit

Path parameters

uuid uuid Required
Order UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/submit', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_submit",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "uuid": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "number": "MS-00042",
    "status": "quoted",
    "business_type": "cafe",
    "scenarios": [
      "service",
      "cleanliness"
    ],
    "goal": "Check the new menu launch",
    "checklist_id": 310,
    "needs_questionnaire": false,
    "questionnaire_brief": null,
    "visits_per_location": 2,
    "period_from": "2026-11-10",
    "period_to": "2026-11-30",
    "time_slots": [
      "weekday",
      "evening"
    ],
    "requirements": {
      "audio": false,
      "receipt": true,
      "place_photos": false,
      "staff": true,
      "express": false
    },
    "shopper_profile": {
      "gender": "any",
      "age_from": 20,
      "age_to": 35
    },
    "legend": null,
    "reimbursement": 5000,
    "currency": "KZT",
    "quote": {
      "currency": "KZT",
      "visits": 6,
      "lines": [
        {
          "key": "visit",
          "qty": 6,
          "unit": 9000,
          "amount": 54000
        }
      ],
      "total": 84000,
      "per_visit": 14000
    },
    "offer": null,
    "total": 84000,
    "contact_name": "Daniyar Omarov",
    "contact_phone": "+7 701 000 00 01",
    "comment": null,
    "payment_status": "none",
    "decline_reason": null,
    "locations": [
      {
        "id": 77,
        "department_id": 12,
        "name": "Abay Ave",
        "city": "Almaty",
        "address": "12 Abay Ave",
        "visits": 2,
        "note": null
      }
    ],
    "problems": {},
    "progress": {
      "done": 0,
      "avg": null,
      "planned": 6
    },
    "submitted_at": "2026-11-03T10:00:00+05:00",
    "confirmed_at": null,
    "launched_at": null,
    "completed_at": null,
    "created_at": "2026-11-02T18:00:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/orders/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
  }
}

POSTAccept the offer of a turnkey order

POST /mystery/orders/{uuid}/accept
Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_accept

Path parameters

uuid uuid Required
Order UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/accept' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/accept', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/accept",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/accept', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_accept",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "uuid": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "number": "MS-00042",
    "status": "quoted",
    "business_type": "cafe",
    "scenarios": [
      "service",
      "cleanliness"
    ],
    "goal": "Check the new menu launch",
    "checklist_id": 310,
    "needs_questionnaire": false,
    "questionnaire_brief": null,
    "visits_per_location": 2,
    "period_from": "2026-11-10",
    "period_to": "2026-11-30",
    "time_slots": [
      "weekday",
      "evening"
    ],
    "requirements": {
      "audio": false,
      "receipt": true,
      "place_photos": false,
      "staff": true,
      "express": false
    },
    "shopper_profile": {
      "gender": "any",
      "age_from": 20,
      "age_to": 35
    },
    "legend": null,
    "reimbursement": 5000,
    "currency": "KZT",
    "quote": {
      "currency": "KZT",
      "visits": 6,
      "lines": [
        {
          "key": "visit",
          "qty": 6,
          "unit": 9000,
          "amount": 54000
        }
      ],
      "total": 84000,
      "per_visit": 14000
    },
    "offer": null,
    "total": 84000,
    "contact_name": "Daniyar Omarov",
    "contact_phone": "+7 701 000 00 01",
    "comment": null,
    "payment_status": "none",
    "decline_reason": null,
    "locations": [
      {
        "id": 77,
        "department_id": 12,
        "name": "Abay Ave",
        "city": "Almaty",
        "address": "12 Abay Ave",
        "visits": 2,
        "note": null
      }
    ],
    "problems": {},
    "progress": {
      "done": 0,
      "avg": null,
      "planned": 6
    },
    "submitted_at": "2026-11-03T10:00:00+05:00",
    "confirmed_at": null,
    "launched_at": null,
    "completed_at": null,
    "created_at": "2026-11-02T18:00:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/orders/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
  }
}

POSTCancel a turnkey order

POST /mystery/orders/{uuid}/cancel
Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_cancel

Path parameters

uuid uuid Required
Order UUID.

Request body

reason string | null
Maximum length 2000.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"reason":"Seal on fridge #2 is torn, temperature rises to +9 °C."},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/cancel', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'reason' => 'Seal on fridge #2 is torn, temperature rises to +9 °C.',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_cancel",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "reason": "Seal on fridge #2 is torn, temperature rises to +9 °C."
        }
    }
}
Response 200
{
  "data": {
    "uuid": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "number": "MS-00042",
    "status": "quoted",
    "business_type": "cafe",
    "scenarios": [
      "service",
      "cleanliness"
    ],
    "goal": "Check the new menu launch",
    "checklist_id": 310,
    "needs_questionnaire": false,
    "questionnaire_brief": null,
    "visits_per_location": 2,
    "period_from": "2026-11-10",
    "period_to": "2026-11-30",
    "time_slots": [
      "weekday",
      "evening"
    ],
    "requirements": {
      "audio": false,
      "receipt": true,
      "place_photos": false,
      "staff": true,
      "express": false
    },
    "shopper_profile": {
      "gender": "any",
      "age_from": 20,
      "age_to": 35
    },
    "legend": null,
    "reimbursement": 5000,
    "currency": "KZT",
    "quote": {
      "currency": "KZT",
      "visits": 6,
      "lines": [
        {
          "key": "visit",
          "qty": 6,
          "unit": 9000,
          "amount": 54000
        }
      ],
      "total": 84000,
      "per_visit": 14000
    },
    "offer": null,
    "total": 84000,
    "contact_name": "Daniyar Omarov",
    "contact_phone": "+7 701 000 00 01",
    "comment": null,
    "payment_status": "none",
    "decline_reason": null,
    "locations": [
      {
        "id": 77,
        "department_id": 12,
        "name": "Abay Ave",
        "city": "Almaty",
        "address": "12 Abay Ave",
        "visits": 2,
        "note": null
      }
    ],
    "problems": {},
    "progress": {
      "done": 0,
      "avg": null,
      "planned": 6
    },
    "submitted_at": "2026-11-03T10:00:00+05:00",
    "confirmed_at": null,
    "launched_at": null,
    "completed_at": null,
    "created_at": "2026-11-02T18:00:00+05:00",
    "url": "https://app.jecheck.io/ui/mystery/orders/0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
  }
}

POSTWrite to the jeCheck manager

POST /mystery/orders/{uuid}/messages
Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_messages_create

Path parameters

uuid uuid Required
Order UUID.

Request body

body string Required
Length 1–5000.

Responses

StatusDescription
201Created
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X POST 'https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/messages' \
  -H 'Authorization: Bearer $JECHECK_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "body": "string"
}'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/messages', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
      "body": "string"
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/messages",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
    json={"body":"string"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('POST', 'mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b/messages', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
    'json' => [
        'body' => 'string',
    ],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_messages_create",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
            "body": "string"
        }
    }
}
Response 201
{
  "data": {
    "id": 2,
    "type": "message",
    "side": "client",
    "author": "Daniyar Omarov",
    "body": "Can we add one more location?",
    "payload": null,
    "at": "2026-11-03T12:00:00+05:00"
  }
}

DELDelete a turnkey order draft

DELETE /mystery/orders/{uuid}

Only drafts can be deleted; submitted orders are cancelled.

Token scopewrite
Permission mystery.orders
MCP toolmystery_orders_delete · destructive

Path parameters

uuid uuid Required
Order UUID.

Responses

StatusDescription
200OK
401unauthorized — Missing, invalid, revoked or expired token
403forbidden · insufficient_scope · plan_required — Token lacks mystery.orders, is read-only
404not_found — The record does not exist or is not visible to the token
422validation_failed — Invalid parameters — see error.fields
429rate_limited — Too many requests — retry after Retry-After seconds
Request
curl -X DELETE 'https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b' \
  -H 'Authorization: Bearer $JECHECK_TOKEN'
const res = await fetch('https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.JECHECK_TOKEN}`,
  },
});
const { data } = await res.json();
import os, requests

res = requests.delete(
    "https://app.jecheck.io/api/ext/v1/mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b",
    headers={"Authorization": f"Bearer {os.environ['JECHECK_TOKEN']}"},
)
res.raise_for_status()
data = res.json()["data"]
$client = new GuzzleHttp\Client(['base_uri' => 'https://app.jecheck.io/api/ext/v1/']);

$res = $client->request('DELETE', 'mystery/orders/3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('JECHECK_TOKEN')],
]);
$data = json_decode($res->getBody(), true)['data'];
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
        "name": "mystery_orders_delete",
        "arguments": {
            "uuid": "3f2b8c1e-7a4d-4e2f-9b1a-6c5d4e3f2a1b"
        }
    }
}
Response 200
{
  "data": {
    "deleted": true
  }
}