Revup API, as shipped in the download
Revup REST API, Webhooks & MCP
Revup ships a JSON REST API and signed webhooks so your booking system, POS or CRM can queue review requests the moment a job finishes — and so a private message from an unhappy customer reaches your helpdesk quickly. Every customer who rates is shown your public review link, whatever the rating.
Authentication
Create a key in Settings → API & Webhooks. Send it on every call:
Authorization: Bearer apk_...
Shown once at creation, stored as a SHA-256 hash. Revoke any time — revoking the key is the off switch.
Endpoints
| Method & path | What it does |
|---|---|
GET /api/ping | Verify a key |
GET /api/contacts | Newest 500 contacts |
POST /api/contacts | Create a contact (name + email or phone) |
GET /api/requests | Newest 200 requests; ?status= filters |
POST /api/requests | Queue (and by default send) review requests for a campaign |
GET /api/feedback | Private messages from the rating page; ?handled=false for the open ones |
GET /api/openapi.json | OpenAPI 3.0 description |
Queue requests after a job completes
curl -X POST https://reviews.example.com/api/requests \
-H "Authorization: Bearer apk_..." -H "Content-Type: application/json" \
-d '{"campaign_id":1,"contact_ids":[12,13,14]}'
Add "send": false to queue without sending — useful if you want a human to review the batch first.
Duplicate-suppressed. This reuses the same core the UI uses, so a contact who already has an open request for that campaign is not queued a second time. The response reports queued, sent, pending and failed counts so you can reconcile.
pending means the channel is not configured (no SMTP / no Twilio) — the request still exists and carries a gate_url you can send yourself.
Read the private messages
curl -H "Authorization: Bearer apk_..." \
"https://reviews.example.com/api/feedback?handled=false"
{ "feedback": [ { "id": 4, "request_id": 19, "contact_id": 7,
"rating": 2, "message": "Waited 40 minutes.",
"handled": false, "created_at": "2026-08-06 11:44:02" } ], "count": 1 }
These are the private messages. A customer who rates below your threshold is shown your public review link like everyone else, and is also offered a private message to you — it lands here. Pull them into your helpdesk and close the loop.
The payload deliberately carries no gate token: a token is a capability to submit a rating, and replaying it is not something an integration should be able to do.
MCP — the agent endpoint (new in 3.0)
Revup speaks MCP (Model Context Protocol) on one route, so an assistant can watch your review pipeline and queue follow-ups without anyone building a bridge first.
POST /mcp
Authorization: Bearer apk_... ← the SAME revocable key the REST API uses
Content-Type: application/json
Stateless streamable-HTTP: one JSON-RPC message per request, no session, no SSE. A GET gets 405 with Allow: POST, as the transport requires — and that answer is unauthenticated, so a client probing for MCP support learns you speak it before it has a key.
Connect it to an assistant
Mint the key in the app first: Settings → API keys. Choose Read only when the assistant should answer questions but never change anything — the endpoint then lists only the read tools and refuses the rest by name, so a careless prompt cannot write. Full access behaves as before.
Every client needs the same three facts, and nothing in the handshake is vendor-specific:
| The address | https://reviews.example.com/mcp |
| The key | header Authorization: Bearer apk_xxxx |
| The transport | MCP over streamable HTTP, stateless |
Claude — one command, or the same URL and header as a custom connector in the desktop and web apps:
claude mcp add --transport http revup https://reviews.example.com/mcp \
--header "Authorization: Bearer apk_xxxx"
ChatGPT and the OpenAI API — one entry in the Responses API's tools array (in ChatGPT itself, the same URL and key go in as a connector):
{
"type": "mcp",
"server_label": "revup",
"server_url": "https://reviews.example.com/mcp",
"authorization": "apk_xxxx",
"require_approval": "never"
}
Own Your AI reads a list of servers in this shape:
{
"mcpServers": [
{ "id": "revup", "name": "Revup", "url": "https://reviews.example.com/mcp",
"token": "apk_xxxx", "enabled": true }
]
}
**Every other client spells the same three facts differently — copy the shape from its own documentation, not from here.** VS Code is the clearest example of why: its configuration reference (read 6 September 2026) puts servers in .vscode/mcp.json under a "servers" object — *"an object that maps server names to their configurations"* — not an mcpServers array. Pasted as-is, the block above will not load there. The id, the URL and the token are what travel; the JSON around them belongs to whichever client you are configuring.
A local model, n8n, or your own code — n8n's MCP Client node takes the URL and the same Authorization: Bearer header; a model running on your own machine reaches it through any MCP client, so nothing leaves your network at all. Writing it yourself is one POST of JSON-RPC 2.0:
curl -X POST https://reviews.example.com/mcp \
-H "Authorization: Bearer apk_xxxx" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Any client that speaks MCP's streamable-HTTP transport works, including ones that do not exist yet: Revup implements the protocol, not an integration with a particular vendor.
The tools
| Tool | Writes? | What it does |
|---|---|---|
list_requests | no | Review requests with status, rating and the customer they belong to. Filter by status, rated, limit. |
request_detail | no | One request, plus any private feedback left against it. |
create_request | YES | Queues a review request for a customer and mints its one-time rating link. |
response_stats | no | Response rate, average rating, how many raters opened your review page and how many left a private message — the dashboard's own figures. |
status is queued (scheduled inside a send window), pending (waiting to be sent or copied), sent, or failed — the same four words the Requests screen uses.
What the endpoint refuses, and why
- It cannot change the rating page. The threshold and the review URL are not writable over MCP. An agent may chase reviews; the page every customer sees stays with the owner.
- No figure is recomputed.
response_statsreturnsRevup::stats()— the same call the dashboard makes — so an agent can never quote a response rate Revup disagrees with. create_requestqueues, it does not send. Sending goes out through your own SMTP, your own Twilio, or as a link you copy; that is the owner's action and the owner's cost.- It writes requests through the product's own creator, so the one-open-request-per-customer rule and token minting apply exactly as they do in the browser. A second request for a customer who already has one open is refused.
- Arguments must be single values, unknown arguments are refused by name, and out-of-enum values are refused with the valid options listed.
- Roles apply exactly as they do in the browser. A viewer's key is refused
create_request.
Every MCP write lands in the audit trail beside the browser and API writes, naming the key's user as the actor and recording via: mcp.
Example
curl -X POST https://your-install/mcp \
-H "Authorization: Bearer apk_..." -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"response_stats","arguments":{}}}'
Webhooks
Add a receiver in Settings → API & Webhooks and choose events:
| Event | Fires when |
|---|---|
request.sent | A review request is dispatched |
feedback.received | A customer sends you a private message from the rating page — wire this to your helpdesk |
rating.public | A customer opens your public review page from the rating page (any rating; once per request) |
Every delivery is signed:
X-Revup-Event: feedback.received
X-Revup-Signature: sha256=<hmac_sha256(raw_body, your_webhook_secret)>
Compare with hash_hmac('sha256', $rawBody, $secret) and reject on mismatch.
Email notifications
Revup already had its own SMTP settings and mailer, so 2.0 adds no new mail configuration — just a notify address and one toggle in Settings: email me the moment a customer sends a private message. That is the one alert where minutes matter.
Errors
| Code | Meaning |
|---|---|
401 | Missing, unknown or revoked key |
422 | Validation — detail names the offending field |