Build a merchant sign-up workflow
Guide a merchant from package-driven application fields through consent, underwriting and readiness to process.
Choose the onboarding workflow
The Sign-Up API is for NMI Payments partners collecting applications for risk review. It covers packages, applications and asynchronous decisions. For an existing merchant account that only needs gateway boarding, use the separate Account Management workflow. Confirm your partner access and enabled package before building a merchant-facing form. This is an implementation guide, not an active application form. Every request shown is illustrative; no merchant has been onboarded by these examples.
Keep partner authentication on your server
Sign-Up uses OAuth client credentials. Exchange client_id and client_secret for access_token, token_type and expires_in. The documented token lifetime is 60 minutes. Request a new token after expiry. These are Sign-Up credentials, not a gateway merchant payment key. Environment Base URL Sandbox https://sandbox.signup.nmi.com/api/v1 Production https://secure.nmi.com/api/v5/signup POST /api/v1/oauth/token HTTP/1.1 Host: sandbox.signup.nmi.com Content-Type: application/json { "grant_type": "client_credentials", "client_id": "<partner-client-id>", "client_secret": "<partner-client-secret>", "scope": ["packages:read", "applications:read", "applications:write"] } Use Authorization: Bearer <access-token> for protected calls. Add subscriptions:read and subscriptions:write only when managing event subscriptions. Recommended implementation: store credentials and tokens outside client-side bundles and logs. Token request fields and errors
Load the package before designing fields
GET /packages lists your available options. GET /packages/{package} returns id, name, description, package_rules, fields and collections. A package determines the required, optional and conditional data. A collection holds repeatable entities rather than a single flat form. Choose an available package and retain its identifier with your application record. Build validation from the returned field formats and package_rules. Render collection records with stable local identifiers so later edits address the intended owner or bank record. Present server validation messages next to the affected input; do not replace package rules with a static universal field list. List packages Package rules and fields
Create a draft and retain its identity
POST /applications accepts package_id with fields and collections. Each fields entry has id and value. Each collection contains id and records; a record has its own id and fields. Use identifiers and value types from the selected package. The returned application includes id, package_id, status, fields, collections, created_at and updated_at. Persist the returned application id before allowing further saves or uploads. A structurally valid request is not proof that every submission requirement is satisfied. Keep merchant data separate from diagnostic logs; record correlation identifiers and validation outcomes instead. Create application payload and examples
Save edits only in the allowed state
PATCH /applications/{application} partially updates an application only while status is draft. Send the fields and collection records you intend to update. A 422 response can identify unknown field names, incorrect value types or broken package rules. PATCH /api/v1/applications/<application-id> HTTP/1.1 Host: sandbox.signup.nmi.com Authorization: Bearer <access-token> Content-Type: application/json Idempotency-Key: <unique-operation-uuid> { "fields": [{ "id": "fld_monthly_volume", "value": "20000" }] } The field in this example is documented, but the selected package remains authoritative. Re-read the application when another session or an underwriting event may have changed its status. Draft update contract
Obtain the application’s legal consent URL
GET /applications/{application}/legal-consent returns an object containing url. It requires applications:read. Complete legal consent before submission. Missing required owner information can produce 422, so resolve those application fields before retrying. Legal consent response and errors
Embed consent and handle signature reset
Load the documented sandbox helper, then pass the returned URL to LegalConsent.load. onAccepted receives accepted: true after signing and false if the signature is reset. Disable submission again on false or onError. onLoad only means the frame loaded. Call LegalConsent.destroy when removing the component. <div id="merchant-consent"></div> <button id="submit-application" type="button" disabled>Submit application</button> <script src="https://sandbox.signup.nmi.com/build/consent-helper-1.0.0.umd.js"></script> <script> const submit = document.getElementById("submit-application"); LegalConsent.load("<returned-consent-url>", { selector: "#merchant-consent" }, { onAccepted: ({ accepted }) => { submit.disabled = !accepted; }, onError: () => { submit.disabled = true; } } ); </script> This snippet controls the consent UI only; it does not submit anything. Your server must still enforce application ownership and handle submission validation. A browser button state is not evidence of approval.
Upload supporting documents in the right state
POST /applications/{application}/documents uses multipart/form-data with required type and file parts. The file accepts PNG, JPG/JPEG or PDF up to 20 MB. Uploads are allowed in draft or underwriter_requested_information. Use a documented type such as Void Check; the full endpoint reference contains the allowed document categories. Let your HTTP library generate the multipart boundary. Do not send a JSON filename in place of binary content. Check the returned document record before telling the merchant the upload succeeded. Recommended UX: show upload progress, validation failure and the selected document category independently. Document categories and multipart schema
Submit once consent and required information are complete
POST /applications/{application}/submit moves a successful application to submitted. Its optional skip_merchant_email boolean query parameter controls skipping the signed-agreement email; choose this deliberately. No JSON request body is defined. NMI recommends three months of bank or processing statements when fld_average_ticket is at least 1000 or fld_high_ticket is at least 2500. Show validation errors from 422 and keep the merchant’s work available for correction. A successful submission means review has begun; it does not mean the merchant can process payments. Submit application contract
Make repeated writes deliberate
Use a unique Idempotency-Key for a logical write. The overview documents replay protection and an Idempotent-Replayed: true response header. Create, draft update, document upload and submit references accept a key of 10–100 characters. Recommended implementation: persist the operation key with its payload before sending. Reuse that key for the same uncertain attempt rather than manufacturing another operation. Use a new key when the merchant changes the intended operation. The /info reference does not explicitly declare this header. Do not assume identical operation-level support where it is not specified.
Drive the interface from current application state
GET /applications/{application} supplies status. Its published enum is draft, submitted, approved, rejected, underwriter_requested_information, requested_information_sent and boarded. Keep state names separate from webhook event names; the decline event is named application.declined, while the application enum uses rejected. State or event Recommended product behavior draft Offer editing, document upload and the consent/submission path. submitted Show review in progress; disable draft-only editing. underwriter_requested_information Offer requested uploads and an explanatory note. requested_information_sent Show that information has been supplied; re-read status for further changes. approved Show the approval decision without declaring processing readiness. boarded Use the boarded/ready-to-process signal for the onboarding handoff. rejected Show an appropriate decision message and support route. Application state response Sign-Up event verification
Respond to an underwriter’s request
When status is underwriter_requested_information, upload the requested documents through the document endpoint and POST /applications/{application}/info with a note. The note must contain 1–10000 characters. This is a separate operation from draft PATCH. POST /api/v1/applications/<application-id>/info HTTP/1.1 Host: sandbox.signup.nmi.com Authorization: Bearer <access-token> Content-Type: application/json { "note": "The requested supporting document has been uploaded for review." } Only send that note after the upload actually succeeds. The response contains code and message rather than the full application. Retrieve the application to refresh state. The endpoint returns a validation error when the application is in the wrong state. Additional information contract
Separate approval, boarding and agreement retrieval
The approval event reports an underwriting decision. The merchant-boarded event specifically reports readiness to process. Subscribe to and verify Sign-Up events using the separate Sign-Up signing contract. Do not apply gateway webhook verification rules to these payloads. After submission, GET /applications/{application}/download-agreement retrieves the signed agreement as binary content with applications:read. Calling it before submission can return 422. Recommended implementation: correlate events to the stored application, handle repeat deliveries safely and refresh application state before presenting the next action. Do not promise a processing time or automatic progression that your enabled partner workflow has not established. Download agreement Sign-Up webhooks
Test the decision branches without confusing them with production risk rules
The sandbox uses fld_monthly_volume to select simulated outcomes: below 5000 produces application.declined; 5000–99999 produces application.approved; at least 100000 produces application.updated.data.requested. Use unambiguous sample values such as 1000, 20000 and 100000. Exercise a draft save, an invalid field and a correction. Complete consent, then reset the signature and confirm submission becomes unavailable. Test invalid document format and excessive size before a valid upload. Submit each sandbox branch and verify your event handler and application status refresh. Exercise requested information and agreement download after submission. These sandbox branches were checked in documentation only. No Sign-Up account test, signature, upload, submission or message was executed for this guide.