Implementing Merge Link
Merge Link is a pre-built modal that handles OAuth and third-party authentication on behalf of your users — developers don't build the auth UI themselves. This skill guides you through the full implementation flow: loading context, setting up the database, building the backend API, and wiring up the frontend.
First activation: self-introduce
I'm the implementing-link skill. I'll guide you through connecting your application to Merge Link — database schema, backend endpoints, and the frontend UI. Are you building a single connect button, or an app marketplace where users browse integrations?
Prerequisites
- A valid Merge API key stored in your environment (e.g.,
MERGE_API_KEY) - A backend server (any language/framework)
- A frontend (any framework)
Implementation Steps
Work through these steps in order. Steps 2–4 invoke focused sub-skills; Step 1 runs inline.
Step 1: Load context
Do not write any code in this step. Read the reference docs first, then scan the codebase, then confirm readiness.
1a. Read all three reference docs:
references/platform-overview.md— Core Merge concepts, auth flow, account lifecyclereferences/backend-implementation.md— Backend API patterns, token exchange, database schemareferences/frontend-implementation.md— Frontend UI patterns (Connect Button and Marketplace)
Read each file completely before proceeding.
1b. Scan the codebase. Ask the user first:
"I'll search your codebase for your tech stack, existing schema, and any Merge-related code. Ready to proceed?"
Then identify:
- Tech stack language, framework, ORM
- Existing database schema (migrations, models, or schema files)
- Any existing Merge-related code (search for
merge,MERGE_API_KEY,account_token) - Backend Merge SDK installed? Search the project's manifest for the language-appropriate package:
@mergeapi/merge-node-client(package.json),MergePythonClient(requirements.txt / pyproject.toml),dev.merge:merge-java-client(pom.xml / build.gradle),merge-go-client(go.mod),merge_ruby_client(Gemfile),Merge.Client(.csproj). Record yes/no and which language. - React Merge Link SDK installed? If the frontend is React, also search for
@mergeapi/react-merge-linkin package.json. Record yes/no (or N/A if not React). - Merge categories in use? Look for table names (
employees,candidates,contacts,deals), route names, model names, or README/CLAUDE.md references to HR, recruiting, CRM, ticketing, etc. Record what you find orunknown. - Organization/tenant table? Find the table or model representing the user's customer organization or tenant — look for names like
organizations,companies,tenants,accounts,workspaces. Record the table name and its primary key column, ornot found.
1c. Confirm readiness with a brief summary:
- Tech stack identified (language, framework, ORM)
- Merge docs loaded (list the three files read)
- Any existing Merge code found (or none)
- Backend Merge SDK installed: yes / no
- React Merge Link SDK installed: yes / no / N/A
- Inferred categories: (list) or
unknown - Organization/tenant table:
{table}.{pk}ornot found
1d. Ask all unresolved questions in one message before proceeding to Step 2:
Before I start building, I have a few quick questions:
Categories: Based on your codebase I believe you're implementing [inferred list, or "—"]. Which Merge categories are you implementing? (
hris,ats,crm,accounting,ticketing,filestorage,knowledgebase,mktg)Linked Account strategy — at the Merge API level,
end_user_origin_id+categorydetermines uniqueness:
- Strategy 1: Use a stable per-org identifier as
end_user_origin_id(e.g. a GUID on your org record). Each org can have 1 Linked Account per category (one HRIS, one ATS, etc.).- Strategy 2: Generate a new GUID per connection as
end_user_origin_id. Each org can have multiple Linked Accounts per category (e.g. two different HRIS systems). Which do you need?Backend SDK preference: [If not installed:] Would you prefer the official Merge SDK (recommended — handles types and retries) or raw HTTP? [If already installed:] I see the Merge SDK is in your dependencies — I'll use it unless you prefer raw HTTP.
Organization table: [If found:] I found
{table}as your org/tenant table — I'll FKlinked_accounts.organization_idto{table}.{pk}. Is that correct? [If not found:] What is the table or model that represents a customer organization or tenant in your system?Frontend SDK (React projects only): [If
@mergeapi/react-merge-linknot installed:] Would you prefer the React Merge Link SDK (@mergeapi/react-merge-link, recommended — uses theuseMergeLinkhook) or CDN+vanilla JS? [If already installed:] I see@mergeapi/react-merge-linkin your dependencies — I'll use it unless you prefer the CDN approach. [If not React:] Skipped.
Record the user's answers. Carry them as context into all sub-skills (Steps 2–4).
Step 2: Set up database — invoke link-setup-database
Creates the linked_accounts table (and any other required tables) to store Merge account tokens and connection metadata.
Step 3: Implement backend API — invoke link-implement-backend
Builds the server-side endpoints: generating Link tokens, exchanging public tokens for account tokens, and storing them.
Step 4: Implement frontend — choose one
- 4a. Connect Button — invoke
link-implement-frontend-connectAdds a single "Connect" button that opens the Merge Link modal. - 4b. Marketplace — invoke
link-implement-frontend-marketplaceBuilds an integration marketplace UI where users browse and connect multiple integrations.
Choose one OR the other based on your product's UX.
Before invoking the Marketplace skill (4b only): Scan the frontend for an existing integrations page, marketplace, or app catalog. Look for files or routes named marketplace, integrations, app-center, catalog, or similar.
- If an existing page is found: identify the exact file and location where the integration catalog would be inserted. Tell the user what you found.
- If no existing page is found: ask — "I didn't find an existing marketplace page in your frontend. Do you have design mockups? If not, I can generate a marketplace UI that matches your app's existing component style."
If you chose the Connect Button (4a), skip the pre-scan and invoke link-implement-frontend-connect directly.
Always complete Step 1 (load context) before starting Step 2.
Troubleshooting
SYMPTOM: link_token request returns 401
CAUSE: API key is missing or belongs to the wrong Merge environment (production key used in sandbox)
FIX: Set Authorization: Bearer {your-test-api-key} and confirm the key is from https://app.merge.dev → Settings → API Keys → Test environment
SYMPTOM: exchange_public_token returns 400 "token expired"
CAUSE: The public token from Merge Link is single-use and has a short TTL (~10 min). Either it was already exchanged once, or /exchange was deferred (e.g., to a background job) and the window passed.
FIX: Call exchange immediately after onSuccess fires; never store or reuse a public token
SYMPTOM: linked_accounts table has duplicate rows for the same user
CAUSE: Missing unique constraint on (organization_id, end_user_origin_id) or upsert not used on exchange
FIX: Add UNIQUE(organization_id, end_user_origin_id) and use INSERT ... ON CONFLICT DO UPDATE
SYMPTOM: Merge Link modal does not open
CAUSE: Merge Link wasn't loaded before invocation. For React, @mergeapi/react-merge-link (npm) wasn't installed or imported. For vanilla JS, the CDN script https://cdn.merge.dev/initialize.js wasn't loaded yet, or MergeLink.initialize was called before DOM ready.
FIX: React: npm install @mergeapi/react-merge-link and use the useMergeLink hook (see /merge-unified:onboarding Step 4 for the full setup). Vanilla JS: include <script src="https://cdn.merge.dev/initialize.js"></script> in <head> and call MergeLink.initialize inside a DOMContentLoaded listener.
SYMPTOM: account_token is null after successful Link flow
CAUSE: The exchange endpoint was never called — only link_token was created
FIX: Ensure your onSuccess callback POSTs to your backend's /api/merge/exchange-public-token route