mcpField
C
create-merchant-platform

create-merchant-platform

github.com/payrocOther048 installsUpdated 3w ago
Repository

About create-merchant-platform

create-merchant-platform guides developers through submitting new merchant onboarding data to the Payroc API. It covers authentication, request fields, idempotency keys, and response and error handling.

Key features

  1. 01Obtain a bearer token
  2. 02Build merchant and account requests
  3. 03Submit with an idempotency key
  4. 04Store returned identifiers
  5. 05Handle validation and API errors

Use cases

  1. 01Submit a new merchant application
  2. 02Set up merchant processing accounts
  3. 03Troubleshoot request validation

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/payroc/skills --skill create-merchant-platform

After installing, type/create-merchant-platformin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
_sources.md
api-schema.md
error-response-format.md
identity-call.md

Details

From the project README

Create Merchant Platform

Version check (run this first)

Before announcing anything or starting the flow, confirm this skill is current:

  1. Read this skill's version from the metadata.version field in the frontmatter above.
  2. Fetch the published copy and read its metadata.version: https://raw.githubusercontent.com/payroc/skills/main/plugins/payroc/boarding/skills/create-merchant-platform/SKILL.md
  3. Compare the two as semantic versions:
    • This version >= published → continue silently, no message. (A developer running an unreleased newer version is expected and fine.)

    • This version < published → tell the developer:

      ⚠️ A newer version of this skill (v<published>) has been published — you're running v<current>. Upgrading is recommended for the best results.

      Then ask whether they'd like to continue with the current version or stop and upgrade first, and honour their answer.

    • Couldn't fetch (offline, network error, 404) → note briefly that the version couldn't be verified and continue.


POST https://api.payroc.com/v1/merchant-platforms initiates the boarding process for a new merchant. Payroc reviews the submission and assigns a merchantPlatformId and processingAccountId — save both, as they're required for terminal ordering and all follow-on boarding operations.

For the complete field reference and an annotated example request, read references/api-schema.md (load it when you need enum values, nested field details, or a full worked example).


Quick reference

POST  https://api.payroc.com/v1/merchant-platforms
Authorization:   Bearer <token>
Idempotency-Key: <uuid-v4>
Content-Type:    application/json

Step 1 — Get a bearer token

Read references/identity-call.md before emitting any auth code. Do not guess the endpoint URL, header name, or response shape — use only what the reference documents.

Tokens expire in ~1 hour. Exchange your API key before each session (or refresh proactively).

# Test / sandbox
curl -X POST https://identity.uat.payroc.com/authorize \
  -H "x-api-key: YOUR_API_KEY"

# Production
curl -X POST https://identity.payroc.com/authorize \
  -H "x-api-key: YOUR_API_KEY"

Response:

{
  "access_token": "eyJhbGc....",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Use Authorization: Bearer <access_token> on every subsequent request.


Step 2 — Build the request body

The body has two required top-level keys:

{
  "business": { ... },
  "processingAccounts": [ { ... } ],
  "metadata": {}
}

metadata is optional — use it to attach your own key/value pairs (e.g. your internal customer ID) that Payroc echoes back in responses.

Business object

Describes the legal entity. Required fields:

FieldTypeNotes
namestringLegal business name
taxIdstringEIN (US)
organizationTypeenumSee schema reference for values
countryOfOperationstring"US"
addressesarrayMust include one entry with "type": "legalAddress"
contactMethodsarrayMust include at least one "type": "email" entry
Processing accounts

Each element provisions one MID. The array must have at least one entry. Key required fields per account:

FieldNotes
doingBusinessAsTrading/DBA name
merchandiseOrServiceSoldPlain-English description
businessStartDateYYYY-MM-DD
timezonee.g. "America/New_York" — see schema reference for full list
addressPhysical business address
contactMethodsEmail required
ownersSee ownership rules below
processingTransaction volumes and card acceptance
fundingBank account and settlement schedule
pricingPricing intent ID or full pricing agreement
signatureHow the merchant signs the contract
addendumsArray — [] if no special forms apply, see Addendums below

businessType (retail, restaurant, internet, moto, lodging, notForProfit) and categoryCode (4-digit MCC integer) are optional — send them when known, since downstream boarding review still uses them. processor (tsys | fiserv, default tsys) is also optional; send it explicitly rather than relying on the default.

Addendums — ask whether the merchant needs any of these forms sent with the agreement: installment payments, money services, telehealth, firearms, pharmacy CNP compliance, CBD, tobacco CNP, donations, Clover equipment, or Roc Giving. addendums is required on the request but [] is a valid answer if none apply. See references/api-schema.md#addendums for the per-type payload shapes (Clover and Roc Giving carry real fields; the rest are attestation-only).

Ownership rules — every processing account needs:

  • Exactly one owner with relationship.isControlProng: true (only one control prong is allowed)
  • At least one owner with relationship.isAuthorizedSignatory: true

A single owner cannot be both — the API rejects an owner with both flags set ("it must be one or the other or neither"), so you need at least two owners (one control prong, a different authorized signatory). Each owner also needs firstName, lastName, dateOfBirth (YYYY-MM-DD), address, identifiers (national ID / SSN), and an email in contactMethods.

Pricing shortcut — if you have a pricing template, use:

{ "type": "intent", "pricingIntentId": "<id>" }

Otherwise provide the full agreement object (see schema reference for structure).

Signature — send a signing link by email with:

{ "type": "requestedViaEmail" }

Or use "requestedViaDirectLink" with a HATEOAS link object if you're managing the redirect.

Volume breakdown — processing.volumeBreakdown.cardPresent + mailOrTelephone + ecommerce must sum to exactly 100.

Amounts are in cents — transactionAmounts.average: 5000 means $50.00.


Step 3 — Send the request

Always generate a fresh UUID v4 for Idempotency-Key. The key is bound to the request body, so reuse the same key only to retry a byte-for-byte identical submission — the API then returns the original response instead of creating a duplicate. On a genuinely new submission, or when you resubmit after fixing a 400 (the corrected body no longer matches the old key), generate a new UUID — reusing the old key with a changed body returns 409.

curl -X POST https://api.payroc.com/v1/merchant-platforms \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: $(uuidgen | tr '[:upper:]' '[:lower:]')" \
  -H "Content-Type: application/json" \
  -d @merchant-payload.json

Step 4 — Handle the response

201 Created — boarding submission accepted:

{
  "merchantPlatformId": "MP-XXXX",
  "processingAccounts": [
    {
      "processingAccountId": "PA-XXXX",
      "status": "entered"   // may also be "pending" depending on timing
    }
  ],
  "links": [ ... ]
}

Persist merchantPlatformId and each processingAccountId immediately. The initial status is typically "entered" (received, not yet reviewed) but may arrive as "pending" depending on processing timing — poll or use webhooks to detect status changes rather than relying on the create response value.

IDs are opaque. The MP-XXXX / PA-XXXX / PI-XXXX forms in these examples are for readability only. Treat every ID as an opaque string whose format is not guaranteed and varies by environment — in UAT they come back as plain integers (e.g. 9815). Don't validate or parse them against a PREFIX-XXXX pattern.


Step 5 — Handle errors

Errors follow RFC 7807 and include a type URL linking to Payroc docs, plus an errors array for validation failures.

StatusScenarioAction
400 validationField issuesFix each field in errors[].parameter; resubmit with a fresh idempotency key (the corrected body needs a new key)
400 idempotencyKeyMissingMissing headerAdd Idempotency-Key: <uuid-v4> to the request
400 addendum/processor validationUnrecognized or duplicate addendums[].type, an addendum missing a required field, or an unrecognized processorNot a fatal or unexpected error — see the addendum/processor error table in references/api-schema.md; fix the flagged field and resubmit
401Token expired or invalidRe-authenticate and get a fresh bearer token
403Insufficient permissionsCheck API key scope; contact Payroc support
409 resourceAlreadyExistsDuplicate merchantCheck if merchant was already boarded; retrieve existing record
409 idempotencyKeyInUseKey reused with different payloadGenerate a new UUID for the new submission
409 taxIdInUseMerchant with that Tax ID existsRetrieve existing merchant platform instead
500Server errorRetry with exponential backoff; surface errors array if present

Reading validation errors — the response uses the RFC 7807 problem-details envelope (type, title, status, detail, instance); Payroc extends it with an errors array that RFC 7807 does not define. Each errors[] item has a parameter (the JSON path of the failing field), a detail (short reason — distinct from the top-level detail), and a message (human-readable explanation). See references/error-response-format.md for the envelope shape and the canonical error type catalog. A real 400 from UAT (an empty body):

{
  "type": "https://docs.payroc.com/api/errors#bad-request",
  "title": "Bad request",
  "status": 400,
  "detail": "One or more validation errors occurred, see error section for more info",
  "instance": "https://api.uat.payroc.com/v1/merchant-platforms",
  "errors": [
    {
      "parameter": "merchantPlatform.business",
      "detail": "Invalid value",
      "message": "'business' must not be empty."
    },
    {
      "parameter": "merchantPlatform.processingAccounts",
      "detail": "Required field not populated",
      "message": "'processingAccounts' must not be empty."
    }
  ]
}

Each parameter path maps to the request body field that failed — use it to pinpoint and fix each issue. Fix all flagged fields in a single corrected payload and resubmit. (The published OpenAPI spec lists only message on each item, but the live API also returns parameter and detail — verified against UAT, 2026-06-16.)


Common pitfalls

  • No control prong / authorized signatory: Both roles are required in every processing account's owners array — but one owner cannot hold both (the API rejects it), so use at least two owners (one control prong, a different authorized signatory)
  • Phone/fax value with non-digits: phone/mobile/fax value must contain digits only — no +, spaces, or punctuation (e.g. 5125550199, not +1 512 555 0199)
  • Volume breakdown doesn't sum to 100: cardPresent + mailOrTelephone + ecommerce must equal exactly 100; if ecommerce > 0, the account's website is required
  • Amounts in cents: All monetary values (transactionAmounts, monthlyAmounts, acceleratedFundingFee) are integers in cents
  • Date format: dateOfBirth and businessStartDate must be YYYY-MM-DD
  • Missing idempotency key: All POST requests require Idempotency-Key or you get a 400
  • Reusing a key for a different payload: Each unique submission needs its own UUID

Full field reference

Read references/api-schema.md for:

  • All enum values (organizationType, businessType, timezone, fundingSchedule, etc.)
  • Complete nested object schemas (owners, processing, funding, pricing, signature)
  • An annotated end-to-end example request body

Comments

Markdown supported