Appearance
Quickstart: Hosted Merchant Onboarding (HMO)
This guide covers everything you need to onboard merchants onto a payment processor through PayBridge's hosted onboarding flow. In HMO, PayBridge creates a hosted application at the processor and returns a URL — the merchant completes the form directly with the processor.
Note: Hosted onboarding is not a currently supported path — it has not been verified against Clearent, and the
application_urlhost shown in the examples below (boarding.clearent.net) is illustrative, not a confirmed hosted-apply host. Use the programmatic onboarding flow (POST /merchants/{id}/onboard) instead — see the Integration Guide §5 (Merchant Onboarding) for the full request and field reference. Tracked in AB#8247.
Prerequisites
- A PayBridge API key (provided during consumer app setup)
- A PayBridge webhook secret (provided during consumer app setup)
- An HTTPS endpoint on your server to receive webhooks
If you don't have these yet, contact the PayBridge team or ask your admin to create a consumer app via POST /admin/apps.
The Flow
Your App PayBridge API Processor (Clearent)
-------- ----------------- --------------------
| | |
|-- 1. POST /merchants ------->| |
|<-- merchant_id --------------| |
| | |
|-- 2. POST /merchants/ | |
| {id}/onboard/hosted ------>|-- creates hosted session --->|
|<-- application_url ----------|<-- merchant number ----------|
| | |
|-- 3. Redirect merchant | |
| to application_url --------|----------------------------->|
| | |
| (Merchant completes form) | |
| | |
| |<-- merchant.approved --------|
|<-- 4. Webhook: -------------| (processor callback) |
| merchant.approved | |
| | |
|-- 5. GET /merchants/ | |
| {id}/onboard/status ------>| |
|<-- status: approved ---------| |Step 1: Register a Merchant
Create a merchant record in PayBridge. This merchant will later be onboarded with a processor.
bash
curl -X POST https://api.nfs-pay.com/merchants \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Requested-With: PayBridge" \
-d '{
"name": "Acme Property Management",
"business_name": "Acme Property Management LLC",
"business_type": "llc",
"email": "payments@acmepm.com",
"phone": "5551234567",
"address": {
"street": "123 Main St",
"city": "Austin",
"state": "TX",
"zip": "78701"
}
}'Response 201 Created:
json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Property Management",
"business_name": "Acme Property Management LLC",
"status": "active",
"processors": [],
"referral_partner_id": null,
"created_at": "2026-03-25T10:00:00Z"
}Save the id — you'll use it in every subsequent call.
Required Fields
| Field | Type | Description |
|---|---|---|
name | string | Display name |
business_name | string | Legal business name |
business_type | string | Entity type: llc, corporation, sole_proprietorship, partnership, non_profit, government or association_estate_trust. Any other value is rejected. |
email | string | Primary contact email |
Optional Fields
| Field | Type | Description |
|---|---|---|
ein | string | Employer Identification Number (can also be collected during onboarding) |
phone | string | Contact phone number |
address | object | Business address (street, city, state, zip, country) |
referral_partner_id | string | Your partner ID for kickback revenue tracking |
referral_code | string | Referral code used during signup |
Step 2: Initiate Hosted Onboarding
Submit the merchant for hosted onboarding. PayBridge creates a hosted application at the processor and returns the application_url.
bash
curl -X POST https://api.nfs-pay.com/merchants/550e8400-e29b-41d4-a716-446655440000/onboard/hosted \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Requested-With: PayBridge" \
-d '{
"processor_type": "clearent",
"is_primary": true,
"dba_name": "Acme Property Management",
"email": "payments@acmepm.com",
"mcc_code": "6513"
}'Response 202 Accepted:
json
{
"processor_id": "660e8400-e29b-41d4-a716-446655440001",
"type": "clearent",
"onboarding_status": "submitted",
"processor_merchant_id": "100209766",
"application_url": "https://boarding.clearent.net/app/...",
"submitted_at": "2026-03-25T10:01:00Z"
}Request Fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
processor_type | string | No | "clearent" | Payment processor to onboard with |
is_primary | bool | No | true | Set as primary processor for this merchant |
dba_name | string | Yes | — | "Doing Business As" name |
email | string | Yes | — | Merchant email (receives processor communications) |
mcc_code | string | No | "6513" | 4-digit Merchant Category Code |
Common MCC Codes
| Code | Category |
|---|---|
6513 | Real estate / property management |
5999 | General retail |
7299 | General services |
1520 | Contractors / construction |
7311 | Advertising services |
Step 3: Direct the Merchant to the Application
Redirect or link the merchant to the application_url from the response. The merchant completes the onboarding form directly with the processor — PayBridge is not involved in this step.
html
<!-- In your app's merchant management UI -->
<a href="https://boarding.clearent.net/app/..."
target="_blank">
Complete Payment Processing Application
</a>Or send the URL via email to the merchant.
What happens next:
- The merchant fills out the processor's application form (business details, banking, KYC)
- The processor reviews the application (typically 1–3 business days)
- PayBridge receives a webhook from the processor when the application is approved or rejected
- PayBridge forwards a standardized webhook to your registered webhook URL
Step 4: Handle Webhooks
When the processor makes a decision, PayBridge forwards a webhook to your registered endpoint.
Webhook Events
| Event | Meaning |
|---|---|
merchant.approved | Application approved — merchant can accept payments |
merchant.rejected | Application denied — check rejection_reason |
Example: merchant.approved
json
{
"event_type": "merchant.approved",
"webhook_id": "990e8400-e29b-41d4-a716-446655440003",
"merchant_id": "550e8400-e29b-41d4-a716-446655440000",
"processor_type": "clearent",
"processor_merchant_id": "100209766",
"onboarding_status": "approved",
"timestamp": "2026-03-27T14:30:00Z"
}Example: merchant.rejected
json
{
"event_type": "merchant.rejected",
"webhook_id": "990e8400-e29b-41d4-a716-446655440004",
"merchant_id": "550e8400-e29b-41d4-a716-446655440000",
"processor_type": "clearent",
"processor_merchant_id": "100209766",
"onboarding_status": "rejected",
"rejection_reason": "Incomplete banking information",
"timestamp": "2026-03-27T14:30:00Z"
}Verifying Webhook Signatures
Every webhook includes an X-PayBridge-Signature header. Verify it using your webhook_secret:
Python:
python
import hashlib
import hmac
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
# In your webhook handler:
body = request.body # raw bytes
sig = request.headers["X-PayBridge-Signature"]
if not verify_webhook(body, sig, WEBHOOK_SECRET):
return Response(status=403)
payload = json.loads(body)
if payload["event_type"] == "merchant.approved":
activate_merchant(payload["merchant_id"])
elif payload["event_type"] == "merchant.rejected":
flag_merchant_for_review(payload["merchant_id"], payload.get("rejection_reason"))Node.js:
javascript
const crypto = require("crypto");
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return `sha256=${expected}` === signature;
}
// In your Express handler:
app.post("/webhooks/pb", express.raw({ type: "*/*" }), (req, res) => {
const sig = req.headers["x-paybridge-signature"];
if (!verifyWebhook(req.body, sig, WEBHOOK_SECRET)) {
return res.sendStatus(403);
}
const payload = JSON.parse(req.body);
if (payload.event_type === "merchant.approved") {
activateMerchant(payload.merchant_id);
}
res.sendStatus(200);
});Important: Return HTTP 200 promptly. If your endpoint doesn't respond within 10 seconds, PayBridge will retry delivery.
Step 5: Check Onboarding Status (Polling)
You can also poll for status instead of (or in addition to) relying on webhooks. The status endpoint actively checks with the processor for updates when the status is still pending.
bash
curl https://api.nfs-pay.com/merchants/550e8400-e29b-41d4-a716-446655440000/onboard/status \
-H "X-API-Key: YOUR_API_KEY"Response 200 OK:
json
{
"merchant_id": "550e8400-e29b-41d4-a716-446655440000",
"processors": [
{
"processor_id": "660e8400-e29b-41d4-a716-446655440001",
"type": "clearent",
"onboarding_status": "approved",
"processor_merchant_id": "100209766",
"submitted_at": "2026-03-25T10:01:00Z",
"approved_at": "2026-03-27T14:30:00Z",
"rejection_reason": null
}
]
}Onboarding Status Values
| Status | Meaning | Action |
|---|---|---|
pending | Application created, waiting for submission | Merchant needs to complete the hosted form |
submitted | Application submitted to processor | Wait for processor review |
pending_review | Under review at processor | Wait (typically 1–3 business days) |
approved | Merchant account approved | Merchant can now accept payments |
rejected | Application denied | Check rejection_reason, re-submit if correctable |
draft | A programmatic boarding step failed partway (the processor application exists but is incomplete) | Recoverable — re-submit POST /merchants/{id}/onboard to resume from where it stopped |
Step 6: Test Your Webhook Handler
Before going live, verify your webhook endpoint works:
bash
curl -X POST https://api.nfs-pay.com/webhooks/test \
-H "X-API-Key: YOUR_API_KEY"
/webhooks/*is server-to-server and CSRF-exempt — noX-Requested-Withheader needed.
Response:
json
{
"delivered": true,
"status_code": 200,
"error": null,
"webhook_url": "https://yoursite.com/webhooks/pb"
}If delivered is false, check the error field and ensure your endpoint is reachable from the internet.
Error Handling
Common Errors
| Status | Endpoint | Cause | Resolution |
|---|---|---|---|
| 401 | All | Missing or invalid X-API-Key | Check your API key |
| 404 | /merchants/{id}/* | Merchant not found (or belongs to a different app) | Verify the merchant ID |
| 409 | /merchants/{id}/onboard/hosted | Merchant already has a processor in a non-retriable status | Check existing processor status first |
| 400 | /merchants/{id}/onboard/hosted | Hosted onboarding creation failed | Check the detail field for specifics |
| 502 | /merchants/{id}/onboard/hosted | Processor communication error | Safe to retry |
Missing the CSRF Header
State-changing requests (POST, PUT, PATCH, DELETE) on consumer-app and customer routes require:
X-Requested-With: PayBridgeIf you forget this header, you'll get a 403 Forbidden response. Server-to-server webhook endpoints (/webhooks/*) and admin Bearer-authenticated endpoints are exempt.
Rate Limits
| Endpoint | Limit |
|---|---|
POST /merchants | 20 requests/min |
POST /merchants/{id}/onboard/hosted | 20 requests/min |
GET /merchants/{id}/onboard/status | 100 requests/min |
POST /webhooks/test | 200 requests/min |
When rate-limited, you receive HTTP 429 with a Retry-After header.
What's Next?
Once a merchant is approved, they can accept payments. See:
- Getting Started — full integration guide including checkout and refunds
- API Reference — complete endpoint documentation
- Webhooks — full webhook reference with all event types and retry behavior