Merge · Capability

Merge Agent Onboarding (Meta-Gateway)

Merge Agent Onboarding — the meta-gateway pattern. Merge stands in front of hundreds of underlying SaaS APIs (Workday, Salesforce, Greenhouse, NetSuite, Notion, Slack, etc.). The agent onboards once at Merge and receives an account_token that fans out across many downstream APIs via Merge's Linked Account model. Distinct from the other adapter shapes because the credential covers the meta-platform, not a single API. Runtime policy enforcement (signature verify, consent check, scope classify, audit emit) lives in the orchestration.steps below — each step that gates issuance carries on_failure: deny. Lint-time validation of this capability shape lives in the companion Polychro ruleset at https://github.com/api-evangelist/posts/blob/main/polychro/agent-onboarding-rules.yaml — Polychro is Naftiko's governance layer, separate from the capability spec, and is the correct home for cross-object consistency rules that apply across every agent-onboarding capability.

Run with Naftiko Merge.devUnified APIMeta-GatewayLinked AccountsAgent OnboardingLLM GatewayAgent HandlerNaftiko Capability

What You Can Do

POST
Onboardagent
/v1/agents/onboard
POST
Revokeagent
/v1/agents/{agent_id}/revoke

MCP Tools

agent-register

agent-revoke

idempotent

Capability Spec

merge-agent-onboarding.yaml Raw ↑
naftiko: 1.0.0-alpha2
info:
  label: Merge Agent Onboarding (Meta-Gateway)
  description: 'Merge Agent Onboarding — the meta-gateway pattern. Merge stands in front of
    hundreds of underlying SaaS APIs (Workday, Salesforce, Greenhouse, NetSuite, Notion,
    Slack, etc.). The agent onboards once at Merge and receives an account_token that fans
    out across many downstream APIs via Merge''s Linked Account model. Distinct from the
    other adapter shapes because the credential covers the meta-platform, not a single API. Runtime policy enforcement (signature verify, consent check, scope classify, audit emit) lives in the orchestration.steps below — each step that gates issuance carries on_failure: deny. Lint-time validation of this capability shape lives in the companion Polychro ruleset at https://github.com/api-evangelist/posts/blob/main/polychro/agent-onboarding-rules.yaml — Polychro is Naftiko''s governance layer, separate from the capability spec, and is the correct home for cross-object consistency rules that apply across every agent-onboarding capability.'
  tags:
  - Merge.dev
  - Unified API
  - Meta-Gateway
  - Linked Accounts
  - Agent Onboarding
  - LLM Gateway
  - Agent Handler
  - Naftiko Capability
  created: '2026-05-28'
  modified: '2026-05-28'
  related:
  - https://apievangelist.com/2026/05/27/automated-agent-onboarding-is-a-naftiko-capability-not-a-gateway-feature/
  - https://github.com/api-evangelist/merge
binds:
- namespace: env
  keys:
    MERGE_API_KEY: MERGE_API_KEY
    MERGE_ACCOUNT_ID: MERGE_ACCOUNT_ID
    AGENT_TRUSTED_ISSUERS: AGENT_TRUSTED_ISSUERS
    AGENT_CONSENT_HASH: AGENT_CONSENT_HASH
capability:
  consumes:
  - type: http
    namespace: merge-api
    baseUri: https://api.merge.dev/api
    description: Merge Unified API — Linked Account provisioning, account-token exchange,
      integration discovery, and linked-account scope adjustments.
    resources:
    - name: create-link-token
      path: /integrations/create-link-token
      operations:
      - name: createlinktoken
        method: POST
        description: Create the magic-link / DCR-shaped initialization token bound to the
          requesting account. The agent receives the link_token in the onboarding response
          and exchanges it for an account_token in the next step.
        outputRawFormat: json
        outputParameters:
        - { name: result, type: object, value: $. }
        inputParameters:
        - { name: body, in: body, type: object, required: true }
    - name: account-token
      path: /integrations/account-token/{public_token}
      operations:
      - name: getaccounttoken
        method: GET
        description: Exchange the public link token for a permanent account_token (the
          credential the agent uses for all subsequent Unified API calls).
        outputRawFormat: json
        outputParameters:
        - { name: result, type: object, value: $. }
        inputParameters:
        - { name: public_token, in: path, type: string, required: true }
    - name: integrations
      path: /integrations
      operations:
      - name: listintegrations
        method: GET
        description: Discover which downstream SaaS providers are available to the agent
          once Linked.
        outputRawFormat: json
        outputParameters:
        - { name: result, type: object, value: $. }
    - name: linked-accounts
      path: /integrations/linked-accounts/{id}
      operations:
      - name: patchlinkedaccount
        method: PATCH
        description: Adjust scope or metadata on the linked account post-creation.
        outputRawFormat: json
        outputParameters:
        - { name: result, type: object, value: $. }
        inputParameters:
        - { name: id, in: path, type: string, required: true }
        - { name: body, in: body, type: object, required: true }
      - name: deletelinkedaccount
        method: DELETE
        description: Revoke the agent's linked account; all downstream SaaS access is
          severed simultaneously.
        outputRawFormat: json
        outputParameters:
        - { name: result, type: object, value: $. }
    authentication:
      type: bearer
      token: '{{env.MERGE_API_KEY}}'
  orchestration:
  - name: onboard-agent
    description: Verify Web Bot Auth, classify scopes against Merge's unified vocabulary,
      create a link_token bound to the agent's operator account, exchange for account_token,
      annotate the linked account with the agent identity metadata, emit audit via Merge's
      own logging surface.
    inputs:
    - { name: signature, type: object, required: true }
    - { name: signature_agent, type: string, required: true }
    - { name: skill_id, type: string, required: true }
    - { name: requested_scopes, type: array, required: true, description: 'Merge unified scopes — e.g. read:hris.employees, write:accounting.invoices.' }
    - { name: end_user_identifier, type: string, required: true, description: 'Merge requires an end-user identifier even for agent flows; typically the operator''s account-level identifier.' }
    - { name: consent_hash, type: string, required: true }
    - { name: contact, type: object, required: true }
    steps:
    - id: verify_signature
      type: builtin.web-bot-auth.verify
      with:
        signature: '${input.signature}'
        agent: '${input.signature_agent}'
        trusted_issuers: '{{env.AGENT_TRUSTED_ISSUERS}}'
      on_failure: deny
    - id: verify_consent
      type: builtin.policy.assert
      with:
        assert: '${input.consent_hash} == {{env.AGENT_CONSENT_HASH}}'
      on_failure: deny
    - id: classify_scopes
      type: builtin.policy.scope-classify
      description: Map Merge unified scopes to (auto-issue | approval-required | forbidden)
        per the provider's declared policy.
      with:
        requested: '${input.requested_scopes}'
    - id: create_link_token
      call: merge-api.createlinktoken
      with:
        body:
          end_user_origin_id: '${input.end_user_identifier}'
          end_user_organization_name: '${input.contact.operator}'
          end_user_email_address: 'agent-${steps.verify_signature.agent_id}@${input.contact.operator}'
          categories: '${steps.classify_scopes.merge_categories}'
          link_expiry_mins: 30
    - id: exchange_for_account_token
      call: merge-api.getaccounttoken
      with:
        public_token: '${steps.create_link_token.link_token}'
    - id: annotate_linked_account
      call: merge-api.patchlinkedaccount
      description: Attach the Web Bot Auth signature metadata + consent hash to the
        linked account so subsequent audit queries can attribute traffic.
      with:
        id: '${steps.exchange_for_account_token.id}'
        body:
          custom_metadata:
            naftiko_agent_id: '${steps.verify_signature.agent_id}'
            naftiko_operator: '${input.contact.operator}'
            naftiko_consent_hash: '${input.consent_hash}'
            naftiko_signature_keyid: '${steps.verify_signature.keyid}'
            naftiko_skill_id: '${input.skill_id}'
    - id: emit_audit
      type: builtin.audit.emit
      description: Merge's own audit/logging picks up the per-vendor calls. The orchestration
        emits a synthetic onboarding event tagged with the agent identity for joinability.
      with:
        target: merge-api.integrations
        custom_event:
          event_type: agent.onboarded
          agent_id: '${steps.verify_signature.agent_id}'
          operator: '${input.contact.operator}'
          merge_categories: '${steps.classify_scopes.merge_categories}'
          linked_account_id: '${steps.exchange_for_account_token.id}'
          consent_hash: '${input.consent_hash}'
    output:
      agent_id: '${steps.verify_signature.agent_id}'
      linked_account_id: '${steps.exchange_for_account_token.id}'
      credential:
        type: Bearer
        header: Authorization
        value: '${steps.exchange_for_account_token.account_token}'
        revocation_url: '/v1/agents/${steps.verify_signature.agent_id}/revoke'
      merge_categories: '${steps.classify_scopes.merge_categories}'
      reachable_integrations: '${steps.classify_scopes.merge_categories}'
  - name: revoke-agent
    description: Delete the linked account; severs access to all downstream SaaS integrations
      in one call.
    inputs:
    - { name: linked_account_id, type: string, required: true }
    steps:
    - id: delete_la
      call: merge-api.deletelinkedaccount
      with:
        id: '${input.linked_account_id}'
    output:
      revoked: true
  exposes:
  - type: rest
    namespace: merge-agent-onboarding-rest
    port: 8080
    resources:
    - path: /v1/agents/onboard
      operations:
      - { method: POST, name: onboardagent, call: orchestration.onboard-agent }
    - path: /v1/agents/{agent_id}/revoke
      operations:
      - { method: POST, name: revokeagent, call: orchestration.revoke-agent }
  - type: mcp
    namespace: merge-agent-onboarding-mcp
    port: 9090
    transport: http
    tools:
    - name: agent-register
      hints: { readOnly: false, destructive: false, idempotent: false }
      call: orchestration.onboard-agent
    - name: agent-revoke
      hints: { readOnly: false, destructive: true, idempotent: true }
      call: orchestration.revoke-agent
  - type: agent-skill
    namespace: merge-agent-onboarding-skill
    description: 'Agent skill at /skills/onboard-agent.md. Merge-specific addendum:
      one onboarding call yields a credential the agent uses for every downstream SaaS Merge
      proxies to; the agent does not need to know which underlying SaaS is behind any
      given Unified API endpoint.'
    skill:
      name: onboard-agent
      file: skills/onboard-agent.md