b2d592afe8
Sticky sidebar nav. Sections: Profile (name/language), Security (email/password change), AI & Models (BYOK keys + per-use-case model prefs), Notifications (push subscribe), Nutrition goals. Sub-pages: API keys, Webhooks.
143 lines
7.6 KiB
TypeScript
143 lines
7.6 KiB
TypeScript
import type { Metadata } from "next";
|
|
|
|
export const metadata: Metadata = {
|
|
title: "Webhook Docs — Epicure",
|
|
};
|
|
|
|
export default function WebhookDocsPage() {
|
|
return (
|
|
<div className="max-w-3xl mx-auto space-y-10 py-8">
|
|
<div>
|
|
<h1 className="text-2xl font-bold mb-2">Webhook Documentation</h1>
|
|
<p className="text-muted-foreground">
|
|
Learn how to integrate Epicure webhooks with your own tools, Zapier, and Make.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Section 1: Webhook Events */}
|
|
<section>
|
|
<h2 className="text-xl font-semibold mb-4">Webhook Events</h2>
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full border-collapse">
|
|
<thead>
|
|
<tr className="bg-muted">
|
|
<th className="text-left p-3 border border-border font-medium">Event</th>
|
|
<th className="text-left p-3 border border-border font-medium">Description</th>
|
|
<th className="text-left p-3 border border-border font-medium">Payload Fields</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td className="p-3 border border-border font-mono text-sm">recipe.created</td>
|
|
<td className="p-3 border border-border text-sm">Fired when a recipe is created</td>
|
|
<td className="p-3 border border-border font-mono text-sm text-muted-foreground">id, title, authorId</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="p-3 border border-border font-mono text-sm">recipe.updated</td>
|
|
<td className="p-3 border border-border text-sm">Fired when a recipe is updated</td>
|
|
<td className="p-3 border border-border font-mono text-sm text-muted-foreground">id, title, authorId</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="p-3 border border-border font-mono text-sm">recipe.published</td>
|
|
<td className="p-3 border border-border text-sm">Fired when recipe visibility becomes public</td>
|
|
<td className="p-3 border border-border font-mono text-sm text-muted-foreground">id, title, authorId</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="p-3 border border-border font-mono text-sm">recipe.deleted</td>
|
|
<td className="p-3 border border-border text-sm">Fired when a recipe is deleted</td>
|
|
<td className="p-3 border border-border font-mono text-sm text-muted-foreground">id, title</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="p-3 border border-border font-mono text-sm">meal_plan.updated</td>
|
|
<td className="p-3 border border-border text-sm">Fired when a meal plan entry changes</td>
|
|
<td className="p-3 border border-border font-mono text-sm text-muted-foreground">weekStart, day, mealType</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="p-3 border border-border font-mono text-sm">shopping_list.completed</td>
|
|
<td className="p-3 border border-border text-sm">Fired when shopping list marked complete</td>
|
|
<td className="p-3 border border-border font-mono text-sm text-muted-foreground">id, name</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="p-3 border border-border font-mono text-sm">comment.added</td>
|
|
<td className="p-3 border border-border text-sm">Fired when a comment is added to your recipe</td>
|
|
<td className="p-3 border border-border font-mono text-sm text-muted-foreground">commentId, recipeId, recipeTitle</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Section 2: Payload Format */}
|
|
<section>
|
|
<h2 className="text-xl font-semibold mb-4">Payload Format</h2>
|
|
<p className="text-sm text-muted-foreground mb-3">
|
|
Every webhook request is a POST with a JSON body in the following shape:
|
|
</p>
|
|
<pre className="bg-muted rounded-lg p-4 overflow-x-auto font-mono text-sm whitespace-pre">{`{
|
|
"event": "recipe.created",
|
|
"payload": { "id": "...", "title": "Pasta Carbonara", "authorId": "..." },
|
|
"timestamp": "2024-01-15T10:30:00.000Z"
|
|
}`}</pre>
|
|
</section>
|
|
|
|
{/* Section 3: Signature Verification */}
|
|
<section>
|
|
<h2 className="text-xl font-semibold mb-4">Signature Verification</h2>
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
Every request includes an{" "}
|
|
<code className="font-mono bg-muted px-1.5 py-0.5 rounded text-xs">X-Epicure-Signature</code>{" "}
|
|
header containing an HMAC-SHA256 digest of the raw request body, signed with your webhook
|
|
secret. Always verify this signature before processing the payload.
|
|
</p>
|
|
|
|
<p className="text-sm font-medium mb-2">TypeScript / Node.js</p>
|
|
<pre className="bg-muted rounded-lg p-4 overflow-x-auto font-mono text-sm whitespace-pre mb-4">{`import crypto from "crypto";
|
|
|
|
function verify(body: string, secret: string, signature: string): boolean {
|
|
const expected = crypto.createHmac("sha256", secret).update(body).digest("hex");
|
|
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
|
|
}`}</pre>
|
|
|
|
<p className="text-sm font-medium mb-2">Python</p>
|
|
<pre className="bg-muted rounded-lg p-4 overflow-x-auto font-mono text-sm whitespace-pre">{`import hmac, hashlib
|
|
|
|
def verify(body: bytes, secret: str, signature: str) -> bool:
|
|
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
|
|
return hmac.compare_digest(signature, expected)`}</pre>
|
|
</section>
|
|
|
|
{/* Section 4: Zapier Integration */}
|
|
<section>
|
|
<h2 className="text-xl font-semibold mb-4">Zapier Integration</h2>
|
|
<ol className="list-decimal pl-6 space-y-2 text-sm mb-6">
|
|
<li>Go to <span className="font-medium">zapier.com</span> and create a new Zap.</li>
|
|
<li>Choose <span className="font-medium">Webhooks by Zapier</span> as the trigger and select <span className="font-medium">Catch Hook</span>.</li>
|
|
<li>Copy the webhook URL provided by Zapier.</li>
|
|
<li>Go to <span className="font-medium">Epicure Settings → Webhooks</span> and add that URL.</li>
|
|
<li>Test the trigger in Zapier to confirm it receives the payload.</li>
|
|
</ol>
|
|
|
|
<h3 className="text-base font-semibold mb-3">Example workflows</h3>
|
|
<ul className="list-disc pl-6 space-y-2 text-sm text-muted-foreground">
|
|
<li><span className="font-medium text-foreground">New Recipe Published</span> → Add row to Notion database</li>
|
|
<li><span className="font-medium text-foreground">Shopping List Completed</span> → Send Slack message to #groceries</li>
|
|
<li><span className="font-medium text-foreground">Comment Added</span> → Send email notification via Gmail</li>
|
|
</ul>
|
|
</section>
|
|
|
|
{/* Section 5: Make (Integromat) */}
|
|
<section>
|
|
<h2 className="text-xl font-semibold mb-4">Make (Integromat)</h2>
|
|
<ol className="list-decimal pl-6 space-y-2 text-sm">
|
|
<li>Go to <span className="font-medium">make.com</span> and create a new scenario.</li>
|
|
<li>Add an <span className="font-medium">HTTP > Watch for Incoming Webhooks</span> module as the trigger.</li>
|
|
<li>Copy the generated webhook URL from the module settings.</li>
|
|
<li>Go to <span className="font-medium">Epicure Settings → Webhooks</span> and paste that URL.</li>
|
|
<li>Run the scenario and trigger a test event in Epicure to verify the connection.</li>
|
|
<li>Add downstream modules (Notion, Slack, Gmail, etc.) to act on the incoming data.</li>
|
|
</ol>
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|