GitHub Issues · recipe

Turn App Store and Google Play reviews into GitHub issues

There is no native GitHub Issues destination. Two setups work today: a signed webhook destination with a small receiver that calls the GitHub Issues API, or an AI agent that reads reviews over MCP and files the GitHub issue with its own GitHub Issues access.

Webhooks: Enterprise plan · MCP: every plan · 14-day free trial

What works today

Webhook + receiver (automatic)
Review Expert POSTs each matching review as signed JSON. Your receiver verifies the signature and creates the GitHub issue.
MCP + agent (on demand)
An agent searches reviews with the Review Expert MCP server and files GitHub issues through its own GitHub Issues connection.
Only the reviews you want
Webhook destinations filter by minimum rating, maximum rating and keywords.

Option 1: webhook destination and a receiver

  1. 1

    Deploy the receiver

    Host the code below anywhere that serves HTTPS (a serverless function is enough). It needs a GitHub Issues token with permission to create issues.
  2. 2

    Add a webhook destination

    In Review Expert open Automation → Manage destinations, choose Webhook and paste your receiver URL. Copy the signing secret shown once after you save it into the receiver's REVIEWEXPERT_WEBHOOK_SECRET.
  3. 3

    Set filters and send a test

    For bug triage, a common filter is ratings 1 to 2 or keywords such as "crash" and "login". Send a test notification (it has "test": true) to check the GitHub issue is created.
Headers on every requestSigned with your destination's secret
X-ReviewExpert-Timestamp: <unix seconds>
X-ReviewExpert-Signature: v1=<hex HMAC-SHA256(secret, "<timestamp>.<raw body>")>
X-ReviewExpert-Event: review.created | review.negative | review.positive
X-ReviewExpert-Delivery: <stable id per destination and review, repeated on retries>
Example body (fields may be added; ignore unknown ones)POST, Content-Type: application/json
{
  "delivery_id": "…",
  "event": "review.negative",
  "events": ["review.created", "review.negative"],
  "created_at": "2026-10-13T08:00:00.000Z",
  "test": false,
  "review": {
    "id": "3f9c2b1e-…",
    "rating": 2,
    "title": "Sync broke",
    "content": "Sync stopped working after the latest update.",
    "author_name": "Alex M.",
    "posted_at": "2026-10-13T07:51:00.000Z",
    "sentiment": "negative",
    "app_version": "4.2.0",
    "country": "US",
    "url": "https://…"
  },
  "property": { "id": "8a71d0c4-…", "name": "Acme Notes", "type": "google-play", "external_id": "com.acme.notes", "icon_url": null, "store_url": "https://…" },
  "links": { "review_url": "https://review.expert/admin/…" }
}
Receiver (Node.js 18+, no dependencies)Verify the signature, then create the GitHub issue
import crypto from "node:crypto";
import http from "node:http";

const SECRET = process.env.REVIEWEXPERT_WEBHOOK_SECRET;
const seen = new Set(); // use a database in production

function verify(raw, headers) {
  const ts = Number(headers["x-reviewexpert-timestamp"]);
  const sig = String(headers["x-reviewexpert-signature"] || "");
  if (!Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const expected = "v1=" + crypto.createHmac("sha256", SECRET).update(ts + "." + raw).digest("hex");
  return sig.split(",").some((s) => s.trim().length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(s.trim()), Buffer.from(expected)));
}

// GitHub REST API. GITHUB_TOKEN: a fine-grained token with "Issues: write" on GITHUB_REPO only.
async function createIssue(title, body) {
  const res = await fetch(`https://api.github.com/repos/${process.env.GITHUB_REPO}/issues`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
      Accept: "application/vnd.github+json",
      "X-GitHub-Api-Version": "2022-11-28",
    },
    body: JSON.stringify({ title: title.slice(0, 250), body, labels: ["app-review"] }),
  });
  return res.ok;
}

http.createServer(async (req, res) => {
  let raw = "";
  for await (const chunk of req) raw += chunk;
  if (req.method !== "POST" || !verify(raw, req.headers)) return res.writeHead(401).end();
  const delivery = req.headers["x-reviewexpert-delivery"];
  if (seen.has(delivery)) return res.writeHead(200).end();
  const { review, property, links } = JSON.parse(raw);
  const title = `[${property.name}] ${review.rating}★ ${review.title ?? (review.content ?? "").slice(0, 60)}`;
  const body = [
    review.content ?? "(no text)",
    "",
    `Rating: ${review.rating}/5 · Version: ${review.app_version ?? "n/a"} · Country: ${review.country ?? "n/a"}`,
    `Review in Review Expert: ${links.review_url}`,
  ].join("\n");
  const ok = await createIssue(title, body);
  if (ok) seen.add(delivery);
  res.writeHead(ok ? 200 : 502).end(); // a non-2xx makes Review Expert retry
}).listen(process.env.PORT || 8080);

Set GITHUB_REPO (owner/name) and GITHUB_TOKEN, a fine-grained personal access token or GitHub App token limited to that repository with Issues: write. Create the app-review label first, or remove it from the request.

Option 2: an agent with MCP

The Review Expert MCP server is live on every plan. Connect it to an agent (for example in Claude, ChatGPT, Cursor or VS Code) that also has a GitHub Issues connection, then ask something like: “Find 1–2 star Google Play reviews from the last 7 days that mention crashes, group them by problem and open one GitHub issue per problem with links to the reviews.”

The agent uses tools such as search_reviews, get_app_reviews and get_review to read reviews; creating the GitHub issue is done by the agent's own GitHub Issues tooling, not by Review Expert. See the tool reference.

Good practice

Always verify the signature
Reject requests with a bad signature or a timestamp older than 5 minutes.
De-duplicate on delivery id
Retries repeat X-ReviewExpert-Delivery. Store it so one review never becomes two GitHub issues.
Least-privilege token
Give the receiver a GitHub Issues token that can only create issues in the target project.
Filter before you file
Use rating and keyword filters so only actionable reviews reach the tracker.

Plans

Webhook destinations are on Enterprise; the MCP server is on every plan.
Prices in USD. Every plan starts with a 14-day free trial and can be cancelled anytime. Need something custom? Email contact@review.expert.
DestinationStarterProEnterprise
Price (monthly / billed annually)$49 / $39 per month$149 / $119 per month$399 / $319 per month
Connected apps2410
Team members515Unlimited
Slack✓✓✓
Linear✓✓✓
Microsoft Teams✗✓✓
Zendesk✗✓✓
Webhooks✗✗✓
Zapier✗Coming soonComing soon
Filters: min rating, max rating, keywords✓✓✓
No native GitHub Issues destination
Review Expert does not connect to GitHub Issues directly. These recipes use the webhook destination and the MCP server, which are live today.

Frequently asked questions

Send the reviews that matter to GitHub Issues

Try Enterprise free for 14 days, set up a webhook destination and point it at your receiver.