Raw
1 ---
2 title: "Funding Method — Bind Payment to Procurement Session"
3 sidebarTitle: "POST /funding-method"
4 description: "Bind a payment method to an active procurement session. Accepts USDC on-chain, card, ACH bank transfer, or ETH wallet. Replaces the deprecated POST /api/payments/route."
5 ---
6
7 Once a procurement session is ready for payment, this endpoint binds a funding method to it. You specify the method type — USDC, card, bank transfer (ACH), or ETH wallet — and supply method-specific fields. For USDC and ETH wallet payments, include the target blockchain network. For ACH bank transfers, include routing and account details. For ETH wallet, include the `0x` address (optional ENS). ProcureNet validates the combination against the session's state and the buyer's entitlements, then advances the session toward payment processing. This endpoint replaces the deprecated `POST /api/payments/route` — if your integration still uses that path, follow the migration guidance at the bottom of this page.
8
9 ## Authentication
10
11 <Warning>
12 This endpoint requires a valid Bearer JWT in the `Authorization` header. Requests without a token, or with an expired or malformed token, receive `401 Unauthorized`.
13 </Warning>
14
15 ```
16 Authorization: Bearer <your_jwt>
17 ```
18
19 ## Path Parameters
20
21 <ParamField path="id" type="string" required>
22 The session ID returned by `POST /api/v7/orchestrate`. The session must be in a state that accepts a funding method (for example, `pending_payment` or `escrow_accepted`).
23 </ParamField>
24
25 ## Request Body
26
27 <ParamField body="method" type="string" required>
28 The payment method to bind to this session. Accepted values:
29
30 | Value | Description |
31 |---|---|
32 | `usdc` | On-chain USDC transfer. Requires the `network` field. |
33 | `card` | Credit or debit card. Network and ACH fields are ignored. |
34 | `bank_transfer` | ACH debit from a US bank account. Requires ACH fields below. Network is ignored. |
35 | `eth_wallet` | Native ETH from a buyer wallet. Requires `wallet_address` and `network`. Optional `ens_name`. |
36 </ParamField>
37
38 <ParamField body="network" type="string">
39 The blockchain network for on-chain funding. Required when `method` is `"usdc"` or `"eth_wallet"`. Ignored for `card` and `bank_transfer`.
40
41 Supported values: `base`, `arbitrum`, `polygon`, `ethereum`.
42
43 <Tip>
44 For USDC, `base` offers the lowest gas fees and is recommended unless your buyer or contract rules require otherwise. For native ETH (`eth_wallet`), default to `ethereum` unless the buyer wallet is L2-native.
45 </Tip>
46 </ParamField>
47
48 ### ACH fields (`method: "bank_transfer"`)
49
50 When `method` is `"bank_transfer"`, include the following fields. They are ignored for `usdc`, `card`, and `eth_wallet`.
51
52 <ParamField body="routing_number" type="string" required>
53 ABA routing transit number for the buyer's US bank. Must be exactly **9 digits**. Leading zeros are significant — send as a string, not a number.
54
55 Example: `"021000021"`
56 </ParamField>
57
58 <ParamField body="account_number" type="string" required>
59 The buyer's bank account number. Digits only; typically 4–17 characters. Do not include spaces or hyphens.
60
61 Example: `"123456789012"`
62 </ParamField>
63
64 <ParamField body="account_type" type="string" required>
65 The type of bank account to debit. Accepted values:
66
67 | Value | Description |
68 |---|---|
69 | `checking` | Checking account |
70 | `savings` | Savings account |
71 </ParamField>
72
73 <ParamField body="account_holder_name" type="string" required>
74 Legal name on the bank account, as it appears at the financial institution. Used for ACH authorization matching. Length 2–100 characters.
75
76 Example: `"Acme Procurement LLC"`
77 </ParamField>
78
79 <Info>
80 ACH credentials are transmitted over TLS and stored only as needed to originate the debit. Full account numbers are never returned in API responses — only a masked last-four form is echoed.
81 </Info>
82
83
84 ### ETH wallet fields (`method: "eth_wallet"`)
85
86 When `method` is `"eth_wallet"`, include the following fields. They are ignored for `usdc`, `card`, and `bank_transfer`.
87
88 <ParamField body="wallet_address" type="string" required>
89 Buyer EVM wallet address that will fund the session with native ETH. Must be a checksum-compatible hex address: `0x` followed by exactly **40** hexadecimal characters (`/^0x[a-fA-F0-9]{40}$/`). Case may be mixed (EIP-55); the API normalizes to lowercase for storage and returns the submitted casing when valid.
90
91 Example: `"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0"`
92 </ParamField>
93
94 <ParamField body="ens_name" type="string">
95 Optional ENS name associated with the wallet (for example `"acme.eth"`). Display and verification aid only — settlement always uses `wallet_address`. If both are provided, the resolved ENS address must match `wallet_address` or the request fails with `422`.
96
97 Example: `"treasury.acme.eth"`
98 </ParamField>
99
100 <ParamField body="network" type="string" required>
101 Chain for the ETH debit. Same enum as USDC: `base`, `arbitrum`, `polygon`, `ethereum`. Prefer `ethereum` for native mainnet ETH unless the buyer wallet is L2-native.
102 </ParamField>
103
104 <Info>
105 ProcureNet never asks for a private key or seed phrase. The buyer signs or authorizes the transfer in their wallet after the funding method is bound. Only the address (and optional ENS) are stored on the session.
106 </Info>
107
108
109 ### Platform fee (optional)
110
111 Optionally attach a platform fee when binding the funding method. Omit these fields to charge no platform fee.
112
113 <ParamField body="fee_bps" type="integer">
114 Platform fee in basis points. `100` = **1%** of the funded amount. Typical range `0`–`10000` (0%–100%). When omitted, no platform fee is applied.
115 </ParamField>
116
117 <ParamField body="fee_recipient" type="string">
118 ETH address that receives the platform fee. Must match `/^0x[a-fA-F0-9]{40}$/`. Defaults to the ProcureNet platform wallet when `fee_bps` is set and this field is omitted:
119
120 `0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778`
121 </ParamField>
122
123 <ParamField body="fee_asset" type="string">
124 Asset used to settle the platform fee. Accepted values:
125
126 | Value | Description |
127 |---|---|
128 | `eth` | Native ETH |
129 | `usdc` | USDC |
130
131 On-chain methods (`usdc`, `eth_wallet`) can take the fee on the **same rail** as the funding method (for example USDC fee on a USDC bind, or ETH fee on `eth_wallet`). For `card` and `bank_transfer`, settlement proceeds are routed to `fee_recipient` **later** (off the primary ACH/card rail) — `fee_asset` still records the intended payout asset.
132 </ParamField>
133
134 <Info>
135 When the session amount is already known, the API may echo `fee_bps`, `fee_recipient`, and a computed `fee_amount` in the response. If amount is not yet known, only the fee configuration fields are echoed.
136 </Info>
137
138 ## Request Examples
139
140 <CodeGroup>
141
142 ```typescript USDC on Base
143 const sessionId = 'sess_abc123';
144
145 const res = await fetch(
146 `https://api.procurenet.io/api/v7/sessions/${sessionId}/funding-method`,
147 {
148 method: 'POST',
149 headers: {
150 'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
151 'Content-Type': 'application/json'
152 },
153 body: JSON.stringify({
154 method: 'usdc',
155 network: 'base',
156 fee_bps: 100, // 1%
157 fee_recipient: '0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778',
158 fee_asset: 'usdc' // same rail as funding
159 })
160 }
161 );
162
163 const result = await res.json();
164 ```
165
166 ```typescript Card Payment
167 const sessionId = 'sess_abc123';
168
169 const res = await fetch(
170 `https://api.procurenet.io/api/v7/sessions/${sessionId}/funding-method`,
171 {
172 method: 'POST',
173 headers: {
174 'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
175 'Content-Type': 'application/json'
176 },
177 body: JSON.stringify({
178 method: 'card',
179 fee_bps: 100,
180 fee_recipient: '0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778',
181 fee_asset: 'usdc' // card/ACH: proceeds settle to fee_recipient later
182 })
183 }
184 );
185
186 const result = await res.json();
187 ```
188
189 ```typescript ACH Bank Transfer
190 const sessionId = 'sess_abc123';
191
192 const res = await fetch(
193 `https://api.procurenet.io/api/v7/sessions/${sessionId}/funding-method`,
194 {
195 method: 'POST',
196 headers: {
197 'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
198 'Content-Type': 'application/json'
199 },
200 body: JSON.stringify({
201 method: 'bank_transfer',
202 routing_number: '021000021',
203 account_number: '123456789012',
204 account_type: 'checking',
205 account_holder_name: 'Acme Procurement LLC',
206 fee_bps: 100,
207 fee_recipient: '0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778',
208 fee_asset: 'usdc'
209 })
210 }
211 );
212
213 const result = await res.json();
214 ```
215
216 ```typescript ETH Wallet
217 const sessionId = 'sess_abc123';
218
219 const res = await fetch(
220 `https://api.procurenet.io/api/v7/sessions/${sessionId}/funding-method`,
221 {
222 method: 'POST',
223 headers: {
224 'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
225 'Content-Type': 'application/json'
226 },
227 body: JSON.stringify({
228 method: 'eth_wallet',
229 wallet_address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
230 ens_name: 'treasury.acme.eth', // optional
231 network: 'ethereum',
232 fee_bps: 100,
233 fee_recipient: '0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778',
234 fee_asset: 'eth' // same rail as funding
235 })
236 }
237 );
238
239 const result = await res.json();
240 ```
241
242 </CodeGroup>
243
244 ### cURL — ACH
245
246 ```bash
247 curl -X POST \
248 https://api.procurenet.io/api/v7/sessions/sess_abc123/funding-method \
249 -H "Authorization: Bearer $JWT" \
250 -H "Content-Type: application/json" \
251 -d '{
252 "method": "bank_transfer",
253 "routing_number": "021000021",
254 "account_number": "123456789012",
255 "account_type": "checking",
256 "account_holder_name": "Acme Procurement LLC",
257 "fee_bps": 100,
258 "fee_recipient": "0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778",
259 "fee_asset": "usdc"
260 }'
261 ```
262
263
264 ### cURL — ETH wallet
265
266 ```bash
267 curl -X POST \
268 https://api.procurenet.io/api/v7/sessions/sess_abc123/funding-method \
269 -H "Authorization: Bearer $JWT" \
270 -H "Content-Type: application/json" \
271 -d '{
272 "method": "eth_wallet",
273 "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
274 "ens_name": "treasury.acme.eth",
275 "network": "ethereum",
276 "fee_bps": 100,
277 "fee_recipient": "0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778",
278 "fee_asset": "eth"
279 }'
280 ```
281
282 ## Response Fields
283
284 <ResponseField name="session_id" type="string" required>
285 Echoes back the session ID.
286 </ResponseField>
287
288 <ResponseField name="method" type="string" required>
289 Confirms the payment method that was bound to the session (`usdc`, `card`, `bank_transfer`, or `eth_wallet`).
290 </ResponseField>
291
292 <ResponseField name="network" type="string">
293 The blockchain network confirmed for on-chain funding. Present when `method` is `"usdc"` or `"eth_wallet"`.
294 </ResponseField>
295
296 <ResponseField name="status" type="string" required>
297 The session status after the funding method was set. Typically `"payment_processing"` once the session advances. For ACH, status may remain `"payment_processing"` until the debit clears (see `ach_status`).
298 </ResponseField>
299
300 <ResponseField name="account_type" type="string">
301 Echo of the submitted account type (`checking` or `savings`). Present only when `method` is `"bank_transfer"`.
302 </ResponseField>
303
304 <ResponseField name="account_holder_name" type="string">
305 Echo of the submitted account holder name. Present only when `method` is `"bank_transfer"`.
306 </ResponseField>
307
308 <ResponseField name="routing_number_last4" type="string">
309 Last four digits of the ABA routing number. Present only when `method` is `"bank_transfer"`.
310 </ResponseField>
311
312 <ResponseField name="account_number_last4" type="string">
313 Last four digits of the bank account number. Present only when `method` is `"bank_transfer"`. Full account numbers are never returned.
314 </ResponseField>
315
316 <ResponseField name="ach_status" type="string">
317 ACH-specific processing state. Present only when `method` is `"bank_transfer"`.
318
319 | Value | Description |
320 |---|---|
321 | `pending` | Debit originated; awaiting bank confirmation. |
322 | `submitted` | Sent to the ACH network. |
323 | `settled` | Funds cleared. |
324 | `returned` | Debit returned (insufficient funds, invalid account, etc.). |
325 </ResponseField>
326
327
328 <ResponseField name="wallet_address" type="string">
329 Echo of the submitted wallet address. Present only when `method` is `"eth_wallet"`.
330 </ResponseField>
331
332 <ResponseField name="wallet_address_short" type="string">
333 Short display form (`0x` + first 4 + `…` + last 4). Present only when `method` is `"eth_wallet"`.
334 </ResponseField>
335
336 <ResponseField name="ens_name" type="string">
337 Echo of the submitted ENS name when provided. Present only when `method` is `"eth_wallet"` and `ens_name` was sent.
338 </ResponseField>
339
340
341 <ResponseField name="fee_bps" type="integer">
342 Echo of the submitted platform fee in basis points when a fee was configured.
343 </ResponseField>
344
345 <ResponseField name="fee_recipient" type="string">
346 Echo of the fee recipient address (submitted or platform default).
347 </ResponseField>
348
349 <ResponseField name="fee_asset" type="string">
350 Echo of the fee asset (`eth` or `usdc`) when provided.
351 </ResponseField>
352
353 <ResponseField name="fee_amount" type="string">
354 Computed fee amount when the session amount is known (string decimal). Omitted when amount is not yet determined.
355 </ResponseField>
356
357 ## Response Examples
358
359 <CodeGroup>
360
361 ```json USDC
362 {
363 "session_id": "sess_abc123",
364 "method": "usdc",
365 "network": "base",
366 "status": "payment_processing",
367 "fee_bps": 100,
368 "fee_recipient": "0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778",
369 "fee_asset": "usdc",
370 "fee_amount": "2.50"
371 }
372 ```
373
374 ```json Card
375 {
376 "session_id": "sess_abc123",
377 "method": "card",
378 "status": "payment_processing",
379 "fee_bps": 100,
380 "fee_recipient": "0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778",
381 "fee_asset": "usdc"
382 }
383 ```
384
385 ```json ACH Bank Transfer
386 {
387 "session_id": "sess_abc123",
388 "method": "bank_transfer",
389 "status": "payment_processing",
390 "account_type": "checking",
391 "account_holder_name": "Acme Procurement LLC",
392 "routing_number_last4": "0021",
393 "account_number_last4": "9012",
394 "ach_status": "pending",
395 "fee_bps": 100,
396 "fee_recipient": "0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778",
397 "fee_asset": "usdc"
398 }
399 ```
400
401 ```json ETH Wallet
402 {
403 "session_id": "sess_abc123",
404 "method": "eth_wallet",
405 "network": "ethereum",
406 "status": "payment_processing",
407 "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
408 "wallet_address_short": "0x742d…bEb0",
409 "ens_name": "treasury.acme.eth",
410 "fee_bps": 100,
411 "fee_recipient": "0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778",
412 "fee_asset": "eth",
413 "fee_amount": "0.0025"
414 }
415 ```
416
417 </CodeGroup>
418
419 ## ACH Validation Rules
420
421 | Field | Rule |
422 |---|---|
423 | `routing_number` | Exactly 9 digits (`/^\d{9}$/`). Must pass the ABA check-digit algorithm. |
424 | `account_number` | 4–17 digits (`/^\d{4,17}$/`). |
425 | `account_type` | One of `checking`, `savings`. |
426 | `account_holder_name` | 2–100 characters after trim; letters, spaces, hyphens, periods, and apostrophes allowed. |
427
428 <Warning>
429 Do not send routing or account numbers as JSON numbers. Leading zeros in ABA routing numbers are significant; always use strings.
430 </Warning>
431
432
433 ## ETH Wallet Validation Rules
434
435 | Field | Rule |
436 |---|---|
437 | `wallet_address` | Required. Must match `/^0x[a-fA-F0-9]{40}$/`. Reject all-zero address (`0x` + 40 zeros). |
438 | `ens_name` | Optional. If present: 3–255 chars, valid ENS-like label (letters, digits, hyphens, dots), typically ending in `.eth`. |
439 | `network` | Required. One of `base`, `arbitrum`, `polygon`, `ethereum`. |
440
441 <Warning>
442 Never collect or transmit private keys, seed phrases, or wallet passwords through this endpoint. Only the public address (and optional ENS) belong in the request body.
443 </Warning>
444
445
446 ## Platform Fee Validation Rules
447
448 | Field | Rule |
449 |---|---|
450 | `fee_bps` | Optional integer. When set: `0`–`10000`. `100` = 1%. |
451 | `fee_recipient` | Optional. When set: `/^0x[a-fA-F0-9]{40}$/`, not all-zero. Defaults to `0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778` when `fee_bps` is set and recipient is omitted. |
452 | `fee_asset` | Optional. One of `eth`, `usdc`. On-chain methods may settle on the same rail; card/ACH route proceeds to `fee_recipient` later. |
453
454 ## Supported Networks (USDC & ETH wallet)
455
456 <CardGroup cols={2}>
457 <Card title="Base" icon="circle-check">
458 Lowest fees. Recommended for most USDC transactions.
459 </Card>
460 <Card title="Arbitrum" icon="circle-check">
461 Low fees, high throughput. Good alternative to Base.
462 </Card>
463 <Card title="Polygon" icon="circle-check">
464 Widely supported. Use when buyer wallets are Polygon-native.
465 </Card>
466 <Card title="Ethereum" icon="circle-check">
467 Highest security and liquidity. Higher gas fees apply.
468 </Card>
469 </CardGroup>
470
471 ## Migration from POST /api/payments/route
472
473 <Note>
474 `POST /api/payments/route` is **deprecated** and will be removed in a future release. Migrate all integrations to the v7 session flow.
475 </Note>
476
477 Replace your existing single-call payment routing with the two-step v7 session pattern:
478
479 <Steps>
480 <Step title="Start an orchestration session">
481 Replace your call to `POST /api/payments/route` with a call to `POST /api/v7/orchestrate`. Pass your transaction amount, currency, and any buyer hints.
482
483 ```typescript
484 // Before (deprecated)
485 await fetch('/api/payments/route', {
486 method: 'POST',
487 body: JSON.stringify({ amount: 500, currency: 'USD', paymentMethod: 'usdc' })
488 });
489
490 // After
491 const session = await fetch('/api/v7/orchestrate', {
492 method: 'POST',
493 body: JSON.stringify({ amount: 500, currency: 'USD', hints: { email: 'buyer@example.com' } })
494 }).then(r => r.json());
495 ```
496 </Step>
497 <Step title="Set the funding method on the session">
498 Take the `session_id` from the orchestrate response and call this endpoint to bind your payment method.
499
500 ```typescript
501 // USDC
502 await fetch(`/api/v7/sessions/${session.session_id}/funding-method`, {
503 method: 'POST',
504 headers: { 'Authorization': `Bearer ${jwt}` },
505 body: JSON.stringify({
506 method: 'usdc',
507 network: 'base',
508 fee_bps: 100,
509 fee_recipient: '0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778',
510 fee_asset: 'usdc'
511 })
512 });
513
514 // ACH
515 await fetch(`/api/v7/sessions/${session.session_id}/funding-method`, {
516 method: 'POST',
517 headers: { 'Authorization': `Bearer ${jwt}` },
518 body: JSON.stringify({
519 method: 'bank_transfer',
520 routing_number: '021000021',
521 account_number: '123456789012',
522 account_type: 'checking',
523 account_holder_name: 'Acme Procurement LLC',
524 fee_bps: 100,
525 fee_recipient: '0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778',
526 fee_asset: 'usdc'
527 })
528 });
529
530 // ETH wallet
531 await fetch(`/api/v7/sessions/${session.session_id}/funding-method`, {
532 method: 'POST',
533 headers: { 'Authorization': `Bearer ${jwt}` },
534 body: JSON.stringify({
535 method: 'eth_wallet',
536 wallet_address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
537 network: 'ethereum',
538 fee_bps: 100,
539 fee_recipient: '0xD0Fb43F2e9b4Dcd4C9FB70DA89385f77086cb778',
540 fee_asset: 'eth'
541 })
542 });
543 ```
544 </Step>
545 </Steps>
546
547 ## Error Codes
548
549 | HTTP Status | Description |
550 |---|---|
551 | `400 Bad Request` | Missing required fields; unrecognized `method`; `method` is `"usdc"` or `"eth_wallet"` but `network` is absent or invalid; `method` is `"bank_transfer"` with invalid ACH fields; or `method` is `"eth_wallet"` with an invalid `wallet_address` / `ens_name`; or invalid `fee_bps` / `fee_recipient` / `fee_asset`. |
552 | `401 Unauthorized` | Missing, expired, or invalid Bearer JWT. |
553 | `403 Forbidden` | The session state does not permit setting a funding method at this point (for example, escrow acceptance is still required). |
554 | `404 Not Found` | No session exists for the provided `id`. |
555 | `409 Conflict` | A funding method is already set for this session. Transition to a new session if you need to change it. |
556 | `410 Gone` | The session has expired. Start a new session with `POST /api/v7/orchestrate`. |
557 | `422 Unprocessable Entity` | ACH details failed bank-side pre-validation (for example, routing number not found in the ABA directory); or ENS name does not resolve to the submitted `wallet_address`. |
558
559 ### ACH-specific error body example
560
561 ```json
562 {
563 "error": {
564 "code": "invalid_routing_number",
565 "message": "routing_number must be a valid 9-digit ABA number",
566 "field": "routing_number"
567 }
568 }
569 ```
570
571 Common ACH field error codes: `invalid_routing_number`, `invalid_account_number`, `invalid_account_type`, `invalid_account_holder_name`, `missing_ach_fields`.
572
573 ### ETH wallet-specific error body example
574
575 ```json
576 {
577 "error": {
578 "code": "invalid_wallet_address",
579 "message": "wallet_address must be 0x followed by 40 hex characters",
580 "field": "wallet_address"
581 }
582 }
583 ```
584
585 Common ETH wallet field error codes: `invalid_wallet_address`, `invalid_ens_name`, `ens_address_mismatch`, `missing_eth_wallet_fields`, `invalid_network`.
586