# Implementation Guide: Meta Pixel & Conversions API (CAPI) Tracking for Shopify
**Brand:** Cedar & Coil
**Product:** Arc Clip Lamp ($68 USD)
**Target Audience:** Renters, students, and makers in compact spaces
**Document Purpose:** Technical implementation and verification guide for establishing privacy-resilient, deduplicated Meta conversion tracking on Shopify.
---
### (1) Recommended Architecture: Redundant Hybrid (Browser Pixel + Server-Side CAPI)
For reliable conversion measurement, Cedar & Coil should implement a **redundant hybrid tracking architecture**: the client-side Meta Pixel running concurrently with server-side Meta Conversions API (CAPI), bound together through deterministic event deduplication.
```
[ User Browser / Device ]
/ \
Client Events (Pixel) / \ Shopify Storefront / Checkout
/ \
v v
[ Meta Graph API ] [ Shopify Backend / Webhook / App ]
^ |
\ / Server-Side Payload (CAPI)
\ / (fbp, fbc, hashed customer params,
\ / event_name, event_id)
\ v
[ Meta Graph API ]
|
v
[ Meta Deduplication Engine ]
Matches: event_name + event_id within 48h
```
#### Why Both Streams Are Needed
1. **Browser Pixel (Client-Side) Strengths & Limits:**
- *Strengths:* Captures native browser-level interactions instantly, reads local storage and session context directly, and sets Meta first-party cookies (`_fbp` and `_fbc`).
- *Limits:* Highly susceptible to browser privacy interventions (Apple Safari ITP capping cookie lifespans, Firefox ETP), ad blockers, network drops, script loading errors, and buyers closing checkout confirmation tabs before the script executes.
2. **Conversions API (Server-Side) Strengths & Limits:**
- *Strengths:* Executes in a secure server environment completely immune to ad blockers, client-side script blockers, or browser tab termination. Transmits verified transaction records directly from Shopify's backend.
- *Limits:* Lacks native browser session context unless explicitly captured and forwarded. Pure server-side setups without browser data often suffer from lower Event Match Quality (EMQ) because client cookies (`_fbp`, `_fbc`), client IP, and user agent must be explicitly captured and mapped.
3. **The Redundant Advantage:**
- Operating both streams in parallel ensures maximum signal resiliency. When browser scripts fire unimpeded, Meta receives immediate client context. When client scripts are blocked or interrupted, the server payload delivers the conversion record.
- When configured correctly with matching deduplication keys, Meta merges the two streams rather than counting double, yielding robust data for ad delivery optimization, audience targeting, and campaign measurement without inflated conversion numbers.
---
### (2) Standard Funnel Events & Parameter Specifications
Cedar & Coil must track the complete DTC e-commerce funnel for the Arc Clip Lamp using Meta standard event names. Custom event names should not be used for core funnel steps, as Meta's ad delivery algorithms optimize specifically against standard events.
| Event Name | Storefront Trigger Point | Primary Purpose | Key Parameters |
|---|---|---|---|
| `ViewContent` | Product Detail Page (PDP) view for Arc Clip Lamp | Dynamic remarketing, audience building, engagement tracking | `content_type`, `contents`, `content_ids`, `content_name`, `content_category`, `value`, `currency` |
| `AddToCart` | Clicking "Add to Cart" or drawer cart addition | Optimization for cart additions, retargeting high-intent drop-offs | `content_type`, `contents`, `content_ids`, `content_name`, `value`, `currency` |
| `InitiateCheckout` | User enters checkout (`/checkout`) | Measuring checkout progression & abandonment | `content_type`, `contents`, `content_ids`, `value`, `currency`, `num_items` |
| `Purchase` | Order status page / webhook confirmation | Conversion attribution, ROAS measurement, value optimization | `content_type`, `contents`, `content_ids`, `value`, `currency`, `num_items`, `order_id` |
#### Parameter Details for Arc Clip Lamp ($68.00 USD)
* **ViewContent:**
- `content_type`: `'product'`
- `content_ids`: `['ARC-CLIP-01']` (must match the exact Product SKU / Variant ID in the Meta Catalog)
- `content_name`: `'Arc Clip Lamp'`
- `content_category`: `'Task Lighting'`
- `value`: `68.00`
- `currency`: `'USD'`
* **AddToCart:**
- `content_type`: `'product'`
- `contents`: `[{ id: 'ARC-CLIP-01', quantity: 1, item_price: 68.00 }]`
- `content_ids`: `['ARC-CLIP-01']`
- `content_name`: `'Arc Clip Lamp'`
- `value`: `68.00` (dynamic total: item price × quantity added)
- `currency`: `'USD'`
* **InitiateCheckout:**
- `content_type`: `'product'`
- `contents`: `[{ id: 'ARC-CLIP-01', quantity: 1, item_price: 68.00 }]`
- `content_ids`: `['ARC-CLIP-01']`
- `value`: `68.00` (cart subtotal before shipping and tax)
- `currency`: `'USD'`
- `num_items`: `1`
* **Purchase (Conversion Event):**
- `value`: Total transaction value (`68.00` or grand total including taxes/shipping based on store accounting convention; subtotal excluding shipping/tax is standard for ad ROAS modeling). Numeric float with two decimals, never a formatted string with currency symbols.
- `currency`: ISO 4217 standard 3-letter currency code: `'USD'`.
- `content_ids`: Array of strings matching the Meta Catalog SKU/item IDs: `['ARC-CLIP-01']`.
- `contents`: Array of line-item objects:
```json
[
{
"id": "ARC-CLIP-01",
"quantity": 1,
"item_price": 68.00
}
]
```
- `content_type`: `'product'`
- `num_items`: Sum of item quantities (`1`).
- `order_id`: Unique transaction identifier (e.g., Shopify Order ID or Name `#1001`). This parameter is also passed into CAPI's deduplication fields.
---
### (3) How Deduplication Works Between Pixel and CAPI
When both the browser Pixel and server-side CAPI transmit the same user action (e.g., a purchase of the Arc Clip Lamp), Meta's processing pipeline must reconcile the two records into a single logical event. Without deduplication, reported conversions and attributed revenue would double.
```
+-----------------------------------------------------------------------------------------+
| META DEDUPLICATION |
| |
| Client Pixel Event: |
| event_name: "Purchase" |
| event_id: "shopify_order_5829103948" -------------------+ |
| | |
| Server CAPI Event: v |
| event_name: "Purchase" [ 48-Hour Deduplication ] |
| event_id: "shopify_order_5829103948" ---------------> [ Window ] |
| | |
| Meta Decision: v |
| 1. If Client arrives first: Process Client, hold for Server. [ Combined Event ] |
| 2. When Server arrives: Match event_name + event_id. Merged parameters |
| 3. Retain rich browser context + verified server revenue. Counted as 1 event. |
+-----------------------------------------------------------------------------------------+
```
#### The Two Mandatory Matching Keys
Meta relies on two identifiers present on both client and server payloads:
1. **`event_name`**: Must be identical case-sensitive strings (e.g., `'Purchase'`).
2. **`event_id`**: A unique string generated per user interaction that is shared across both client-side and server-side payloads.
- For `Purchase`: Use the unique Shopify Order ID (e.g., `shopify_order_5829103948`).
- For Storefront Events (`ViewContent`, `AddToCart`): Generate a cryptographically random UUIDv4 or deterministic composite string (e.g., `arc_clip_pdp_${session_id}_${timestamp}`) and pass it simultaneously to `fbq('track', 'ViewContent', {...}, {eventID: event_id})` and the server CAPI payload.
#### The 48-Hour Deduplication Window
- Meta maintains a **48-hour deduplication window**. If the client Pixel fires at $T=0$ and the server CAPI fires at $T=10\text{ seconds}$ (or even several hours later due to queue delays), Meta identifies matching `event_name` and `event_id` keys received by the same Pixel ID within 48 hours.
- Meta merges the records: it attributes the event using the client-side session parameters while updating or corroborating it with server-side customer match parameters and final revenue values.
- If an event is received outside the 48-hour window or with differing `event_id` values, Meta treats them as distinct occurrences, causing conversion overcounting.
---
### (4) Customer Information Preparation & Normalization (User Data Parameters)
To maximize Event Match Quality (EMQ)—which enables Meta to link events back to specific Facebook/Instagram user accounts—customer parameters must be formatted, normalized, and hashed according to Meta's strict specifications before transmission to CAPI.
#### Normalization and Hashing Rules
All PII fields (except IP address, User Agent, and Meta cookies) must be normalized and hashed using **SHA-256** (`hash('sha256', normalized_value)`). Meta requires hexadecimal encoding of the binary hash output (lowercase, 64 characters).
```
Raw Input: " Jane.Doe@Example.COM "
│
▼ 1. Trim whitespace: "Jane.Doe@Example.COM"
│
▼ 2. Lowercase: "jane.doe@example.com"
│
▼ 3. SHA-256 Hash: "2033bc6f3df934f71a073577d61eb42701e6ee448c5e69e4f509e53372c08c84"
```
| Parameter Key | User Data Type | Normalization Rule | Hashing Required? | Example Raw $\to$ Processed |
|---|---|---|---|---|
| `em` | Email Address | Trim leading/trailing whitespace, convert all characters to lowercase. | **Yes** (SHA-256) | `" Jane.Doe@Example.COM "` $\to$ `hash("jane.doe@example.com")` |
| `ph` | Phone Number | Remove all non-numeric characters (spaces, dashes, parentheses). Include country code. Remove leading zeros. | **Yes** (SHA-256) | `"+1 (555) 234-5678"` $\to$ `hash("15552345678")` |
| `fn` | First Name | Trim whitespace, lowercase, remove punctuation. | **Yes** (SHA-256) | `" Jane "` $\to$ `hash("jane")` |
| `ln` | Last Name | Trim whitespace, lowercase, remove punctuation. | **Yes** (SHA-256) | `" Doe "` $\to$ `hash("doe")` |
| `ct` | City | Lowercase, trim whitespace, remove all spaces and punctuation. | **Yes** (SHA-256) | `" San Francisco "` $\to$ `hash("sanfrancisco")` |
| `st` | State / Province | Lowercase, two-letter abbreviation (for US/Canada). | **Yes** (SHA-256) | `" California "` $\to$ `hash("ca")` |
| `zp` | Zip / Postal Code | Lowercase, trim spaces, keep standard formatting (e.g., 5-digit US zip). | **Yes** (SHA-256) | `" 94107 "` $\to$ `hash("94107")` |
| `country` | Country | Lowercase two-letter ISO 3166-1 alpha-2 country code. | **Yes** (SHA-256) | `" US "` $\to$ `hash("us")` |
| `client_ip_address` | IP Address | IPv4 or IPv6 format. Passed directly from server request header. | **No** (Do NOT hash) | `"198.51.100.42"` |
| `client_user_agent` | User Agent | Full browser User Agent string from HTTP request header. | **No** (Do NOT hash) | `"Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 ...)"` |
| `fbp` | Browser Cookie | First-party cookie `_fbp` created by Meta Pixel in visitor's browser. | **No** (Do NOT hash) | `"fb.1.1712000000.123456789"` |
| `fbc` | Click ID Cookie | First-party cookie `_fbc` created when a user lands with a `fbclid`. | **No** (Do NOT hash) | `"fb.1.1712000000.IwAR0123456789"` |
#### Critical Importance of `fbp` and `fbc`
- **`_fbp` (Browser ID):** Set on the first-party domain when a user lands on the Cedar & Coil site. Persisting and forwarding this identifier to CAPI connects the server payload to the browser session.
- **`_fbc` (Click ID):** Generated when a visitor clicks a Meta ad containing a `fbclid` query parameter (`fb.{subdomain_index}.{creation_time}.{fbclid}`). Passing `fbc` gives Meta a deterministic 1:1 link between the ad click and the downstream purchase.
---
### (5) Concrete Shopify Setup Path: Architecture Comparison & Code Implementation
#### Comparison: Native Meta Sales Channel App vs. Custom CAPI
| Dimension | Native Meta Sales Channel App ("Maximum" Data Sharing) | Custom Implementation (Webhooks + Cloudflare Worker / Server GTM) |
|---|---|---|
| **Setup & Maintenance Overhead** | **Near Zero:** Direct OAuth installation from Shopify App Store. Zero server infrastructure or API token rotations to manage. | **High:** Requires standing up, securing, and maintaining custom infrastructure (Server GTM on GCP/Stape or Cloudflare Workers). |
| **Deduplication Reliability** | **Built-in & Managed:** Shopify automatically coordinates `event_id` generation between the storefront script/pixel and backend CAPI calls. | **Manual:** Requires engineering custom code to generate and synchronize identical `event_id`s across theme scripts and server webhooks. |
| **Event Match Quality (EMQ)** | **High Out-of-the-Box:** In "Maximum" mode, Shopify forwards hashed customer profile data, `_fbp`, `_fbc`, IP, and user agent automatically. | **High (if properly engineered):** Can achieve high EMQ, but requires custom code to capture client cookies and headers at checkout start. |
| **Cost** | **Free:** Included with Shopify and Meta Business Manager accounts. | **Ongoing Cloud Costs:** Hosting fees ($20–$120/mo) for GTM servers/Workers, domain routing, and developer maintenance hours. |
| **Flexibility / Customization** | **Low:** Rigid standard schemas. Difficult to add bespoke non-standard parameters or filter sensitive line items. | **Complete:** Total control over payload transformation, data routing to multiple vendors, and parameter enrichment. |
| **Checkout Extensibility Support** | **Full Native Support:** Automatically compatible with Shopify’s Checkout Extensibility and Web Pixels API (`analytics.subscribe`). | **Complex:** Requires building and maintaining a custom Shopify Web Pixel extension to capture checkout milestones. |
#### Recommendation for Cedar & Coil
**Deploy the Native Meta Sales Channel App with Data Sharing set to "Maximum".**
**Rationale:**
As a focused DTC brand launching a single hero product (the Arc Clip Lamp at $68), Cedar & Coil’s operational priority must be marketing execution, creative testing, and clean data capture—not maintaining custom server infrastructure. The native Meta app handles the end-to-end integration:
1. It injects the client Pixel using Shopify's sandboxed Web Pixels API.
2. It transmits server-side CAPI events directly from Shopify's application servers.
3. It guarantees unified `event_id` deduplication across `ViewContent`, `AddToCart`, `InitiateCheckout`, and `Purchase`.
4. Setting Data Sharing to **"Maximum"** turns on Conversions API and feeds all available hashed match keys (email, phone, address, `_fbp`, `_fbc`) to Meta without writing boilerplate code.
---
#### Custom Code Sketch: Server-Side Purchase Event Request (Node.js / Express or Cloudflare Worker)
If Cedar & Coil ever requires a dedicated server endpoint (e.g., inside a custom backend processing Shopify `orders/paid` webhooks), here is the production-grade Node.js implementation:
```javascript
/**
* Cedar & Coil - Meta Conversions API (CAPI) Server-Side Purchase Dispatcher
* Environment: Node.js (v18+)
* Dependency: crypto (built-in standard library)
*/
import crypto from 'node:crypto';
// Configuration
const META_PIXEL_ID = process.env.META_PIXEL_ID || '123456789012345';
const META_CAPI_ACCESS_TOKEN = process.env.META_CAPI_ACCESS_TOKEN || 'EAA...';
const GRAPH_API_VERSION = 'v19.0';
const CAPI_ENDPOINT = `https://graph.facebook.com/${GRAPH_API_VERSION}/${META_PIXEL_ID}/events`;
/**
* Normalizes and hashes customer PII according to Meta CAPI specifications.
* Returns null if input is missing or empty.
*/
function hashUserData(value, type) {
if (!value || typeof value !== 'string') return null;
let normalized = value.trim().toLowerCase();
if (type === 'phone') {
// Retain only digits, remove leading zeros
normalized = normalized.replace(/\D/g, '').replace(/^0+/, '');
} else if (type === 'state') {
// Standardize 2-letter state code
normalized = normalized.slice(0, 2);
}
return crypto.createHash('sha256').update(normalized).digest('hex');
}
/**
* Handles incoming Shopify order webhook and forwards a Purchase event to Meta CAPI.
*
* @param {Object} shopifyOrder - Standard Shopify Order JSON payload (e.g., orders/paid)
* @param {Object} clientContext - Client context extracted from checkout session/cookies
*/
export async function sendMetaPurchaseEvent(shopifyOrder, clientContext = {}) {
const currentTimestamp = Math.floor(Date.now() / 1000);
// 1. Prepare and hash customer PII
const customer = shopifyOrder.customer || {};
const shippingAddress = shopifyOrder.shipping_address || shopifyOrder.billing_address || {};
const userData = {
// Hashed PII parameters
em: customer.email ? [hashUserData(customer.email, 'email')] : undefined,
ph: (customer.phone || shippingAddress.phone)
? [hashUserData(customer.phone || shippingAddress.phone, 'phone')]
: undefined,
fn: (customer.first_name || shippingAddress.first_name)
? [hashUserData(customer.first_name || shippingAddress.first_name, 'string')]
: undefined,
ln: (customer.last_name || shippingAddress.last_name)
? [hashUserData(customer.last_name || shippingAddress.last_name, 'string')]
: undefined,
ct: shippingAddress.city ? [hashUserData(shippingAddress.city, 'string')] : undefined,
st: shippingAddress.province_code ? [hashUserData(shippingAddress.province_code, 'state')] : undefined,
zp: shippingAddress.zip ? [hashUserData(shippingAddress.zip, 'string')] : undefined,
country: shippingAddress.country_code ? [hashUserData(shippingAddress.country_code, 'string')] : undefined,
// Unhashed browser & network context
client_ip_address: clientContext.ipAddress || shopifyOrder.browser_ip || null,
client_user_agent: clientContext.userAgent || shopifyOrder.client_details?.user_agent || null,
fbp: clientContext.fbpCookie || null,
fbc: clientContext.fbcCookie || null
};
// Clean undefined keys
Object.keys(userData).forEach((key) => userData[key] === undefined && delete userData[key]);
// 2. Map line items to Meta catalog objects
const contents = (shopifyOrder.line_items || []).map((item) => ({
id: String(item.sku || item.variant_id || 'ARC-CLIP-01'),
quantity: item.quantity,
item_price: parseFloat(item.price)
}));
const contentIds = contents.map((c) => c.id);
// 3. Construct CAPI event payload with exact deduplication event_id
const eventPayload = {
data: [
{
event_name: 'Purchase',
event_time: currentTimestamp,
event_source_url: `https://cedarandcoil.com/orders/${shopifyOrder.id}`,
action_source: 'website',
// Deduplication ID: Must match the eventID emitted by client-side Pixel
event_id: `shopify_order_${shopifyOrder.id}`,
user_data: userData,
custom_data: {
currency: (shopifyOrder.currency || 'USD').toUpperCase(),
value: parseFloat(shopifyOrder.current_total_price || shopifyOrder.total_price || 68.00),
content_type: 'product',
content_name: 'Arc Clip Lamp',
contents: contents,
content_ids: contentIds,
num_items: contents.reduce((acc, item) => acc + item.quantity, 0),
order_id: String(shopifyOrder.name || shopifyOrder.order_number || shopifyOrder.id)
}
}
]
};
// 4. Transmit payload to Meta Graph API
try {
const response = await fetch(`${CAPI_ENDPOINT}?access_token=${META_CAPI_ACCESS_TOKEN}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(eventPayload)
});
const result = await response.json();
if (!response.ok) {
console.error('[Meta CAPI Error] Failed to send Purchase event:', result);
return { success: false, error: result };
}
console.log('[Meta CAPI Success] Purchase event delivered:', result);
return { success: true, result };
} catch (err) {
console.error('[Meta CAPI Network Error]:', err);
throw err;
}
}
```
---
### (6) End-to-End Validation Protocol (Before Scaling Ad Spend)
Do not scale paid ad spend until the setup passes all five verification stages:
```
[ Stage 1 ] [ Stage 2 ] [ Stage 3 ] [ Stage 4 ]
+----------------------+ +----------------------+ +----------------------+ +----------------------+
| Meta Pixel Helper |-->| Test Events Tool |-->| Deduplication Match |-->| Event Match Quality |
| Verify browser firing| | Live Browser + Server| | Verify status "Both" | | EMQ Score >= 8.0/10 |
+----------------------+ +----------------------+ +----------------------+ +----------------------+
│
▼
[ Stage 5 ]
+----------------------+
| Catalog Match Audit |
| content_ids = Catalog|
+----------------------+
```
1. **Client-Side Inspection (Meta Pixel Helper):**
- Install the Meta Pixel Helper extension in Chrome.
- Browse the Cedar & Coil storefront to the Arc Clip Lamp PDP.
- Verify green status for `ViewContent`. Inspect the payload: `content_type: 'product'`, `content_ids: ['ARC-CLIP-01']`, `value: 68.00`, `currency: 'USD'`.
- Click "Add to Cart" and verify `AddToCart` fires. Proceed to checkout and confirm `InitiateCheckout` fires.
2. **Meta Events Manager "Test Events" Console:**
- In Meta Events Manager, select the Cedar & Coil Pixel/Dataset $\to$ **Test Events** tab.
- Use the **Confirm Website Events** tool: open `https://cedarandcoil.com?fbclid=IwAR_TEST_CLICK_ID` to populate the `_fbc` cookie.
- If testing custom CAPI calls, supply the temporary `test_event_code` parameter.
- Place a test purchase through Shopify (using Bogus Gateway or Shopify Payments Test Mode).
3. **Deduplication Verification:**
- In the Test Events log for `Purchase`, observe the **Receiving Method** column.
- The event must display **`Browser • Server`** (or **`Both`**).
- Expand the event details and confirm:
- The Browser event and Server event carry the exact identical **Event ID** (e.g., `shopify_order_5829103948`).
- Meta explicitly marks the status as **"Deduplicated"**.
- The aggregate conversion count increments by **1**, never 2.
4. **Event Match Quality (EMQ) Audit:**
- In Events Manager, switch to the **Overview** tab after processing test events.
- Check the **Event Quality** score for `Purchase`.
- Target an **EMQ score of 8.0 / 10 or higher**.
- Verify that all core match keys are recognized: `Email (em)`, `Phone (ph)`, `Client IP`, `User Agent`, and `Browser ID (_fbp)` (and `_fbc` when arriving via an ad click).
5. **Catalog Match & Diagnostics:**
- Check the **Diagnostics** tab in Events Manager. Verify there are zero errors regarding mismatched `content_ids` or unmapped catalog items.
- Confirm that `ARC-CLIP-01` correctly resolves to the active Arc Clip Lamp listing in the Meta Commerce Manager Catalog.
---
### (7) Consent, Privacy & Regulatory Compliance Note
A technical tracking implementation is incomplete without proper consent orchestration. Tracking the Arc Clip Lamp across US and international audiences requires technical alignment with applicable privacy frameworks:
1. **Regulatory Requirements:**
- **GDPR / ePrivacy Directive (EU/UK):** Requires prior, explicit, affirmative opt-in consent before reading or setting non-essential cookies (`_fbp`, `_fbc`) or sending customer data to Meta.
- **CCPA / CPRA (California & US State Laws):** Requires providing consumers with clear notice and an opt-out mechanism for the "sale" or "sharing" of personal data for cross-context behavioral advertising.
2. **Technical Shopify Implementation:**
- **Consent Management Platform (CMP):** Deploy a Shopify-compatible CMP (such as the Shopify Privacy & Compliance App, Cookiebot, OneTrust, or Pandectes).
- **Shopify Customer Privacy API:** The native Meta Sales Channel App automatically listens to `window.Shopify.customerPrivacy`. When an EU/UK visitor denies tracking consent, Shopify blocks the browser Pixel from loading and suppresses CAPI event transmission (or strips user identifiers).
3. **Meta Limited Data Use (LDU) for California / US States:**
- Meta supports an account-level and payload-level flag called **Limited Data Use (LDU)** to comply with CCPA/CPRA regulations.
- For orders originating from California where the user has opted out of data sharing, pass the data processing options parameters in the CAPI payload:
```json
"data_processing_options": ["LDU"],
"data_processing_options_country": 1,
"data_processing_options_state": 1000
```
*(Setting `[0]` allows Meta to automatically detect whether the user is located in California).*
4. **Data Minimization & Policy Alignment:**
- Never transmit unhashed PII under any circumstance.
- Never pass sensitive payment details (credit card numbers, CVVs, bank details) into custom parameters.
- Ensure Cedar & Coil's Privacy Policy explicitly discloses third-party data sharing with Meta for ad measurement, custom audiences, and dynamic retargeting.
---
*This guide provides the complete blueprint for Cedar & Coil's tracking foundation. The full markdown file has also been saved to:*
`/data/.openclaw/workspace/resources/evals/20260904-201447-55a6e18f/shopify-meta-capi/implementation-guide.md`