Reserve Funding API
The Reserve Funding API allows an external reserve funding service to submit reserve funding requests to Netevia HQ and retrieve the current status of previously submitted requests.
The API supports two operations:
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/reserve-funding/requests | Submit a new Reserve Funding request |
GET | /api/v1/reserve-funding/requests/{externalRequestId} | Retrieve the current status of a Reserve Funding request |
The GET endpoint is the authoritative reconciliation mechanism. The decision callback is provided as a best-effort notification and should not be treated as the system of record.
Base URL
Staging
https://hq.staging.netevia.devProduction uses a different base URL, credentials, and department IDs.
The API uses the same request and response structure across environments.
Authentication
The Reserve Funding API uses HTTP Basic Authentication.
Each integration should use a dedicated Netevia API user.
Authorization: Basic <base64(username:password)>
Content-Type: application/json
Accept: application/jsonThe API user must have:
- General Netevia API access.
Reserve Funding API accesspermission.
Without general API access, the API returns 401.
Without the Reserve Funding API permission, the API returns 403.
Authentication errors
| HTTP status | Error | Description |
|---|---|---|
401 | Authentication failure | Invalid credentials, unknown user, or missing authentication header |
401 | Using of API is not allowed | The user is valid but API access is disabled or restricted |
403 | The authenticated user does not have the reserve-funding permission. | The user does not have access to the Reserve Funding API |
These errors indicate an account/configuration issue rather than a problem with the request payload.
Reserve Funding Workflow
The complete workflow is:
- The merchant signs the Reserve Funding agreement.
- The external service submits the request using
POST. - Netevia HQ validates the request and downloads the signed document.
- A request is created in Netevia HQ with status
Pending. - A Netevia employee reviews the request.
- The employee either:
- approves the request, or
- rejects the request with a reason.
- For an approved request, Netevia creates the Reserve Funding Settings on the merchant profile.
- Netevia sends a decision callback to the external service.
- The external service can use the
GETendpoint to reconcile the final state.
The GET endpoint can be used at any point in this process and is the authoritative source for the request status.
1. Submit Reserve Funding Request
POST /api/v1/reserve-funding/requestsSubmits a Reserve Funding request to Netevia HQ.
The request contains:
- an external request identifier;
- merchant information;
- the Reserve Funding department;
- reserve deduction terms;
- the signed agreement;
- a contact email.
After successful validation, Netevia downloads and stores the document and creates a Pending request for review.
Request
{
"externalRequestId": "RF-2026-09-1029",
"merchant": {
"mid": "9180465590107730",
"dbaName": "The Best Tires"
},
"fundingDepartment": {
"departmentId": "STG824-RSV-9180",
"departmentName": "Reserve API Approval"
},
"fundingTerms": {
"startDate": "2026-09-15",
"stopDate": "2026-12-31",
"percent": 10,
"ceiling": 25000
},
"document": {
"fileName": "Reserve-Agreement-The-Best-Tires.pdf",
"fileType": "pdf",
"fileUrl": "https://files.example.com/agreements/best-tires.pdf"
},
"email": "[email protected]"
}This example follows the request structure and sample data from the Reserve Funding Handbook.
Request Fields
externalRequestId
externalRequestIdType: string
Required: Yes
Maximum length: 64 characters
A unique identifier generated by the external service.
The value is used for:
- identifying the request;
- idempotency;
- retrieving the request using the
GETendpoint.
The value is trimmed before it is stored and compared.
The identifier is unique within the API user.
Idempotency
externalRequestId is the idempotency key.
If the same value is submitted again, Netevia does not create another request. Instead, the existing request is returned with:
"duplicate": trueThe returned status represents the current status of the existing request.
For example, if the original request has already been approved:
{
"data": {
"requestId": "HQ-RF-REQ-101",
"externalRequestId": "RF-2026-09-0001",
"status": "Approved",
"duplicate": true
},
"requestId": 407170,
"success": true
}Therefore, retrying a request with the same externalRequestId is safe and is the recommended way to recover from a network timeout.
externalRequestId is safe and is the recommended way to recover from a network timeout.merchant
merchantType: object
Required: Yes
Contains information about the merchant for which the reserve will be created.
merchant.mid
merchant.midType: string
Required: Yes
Maximum length: 32 characters
The merchant's Netevia MID.
The MID must exactly match an existing merchant.
The lookup is not trimmed. Leading or trailing whitespace can therefore cause the request to fail.
Example:
"merchant": {
"mid": "9180465590107730",
"dbaName": "The Best Tires"
}If the MID cannot be found:
merchant not foundis returned as a validation error.
merchant.dbaName
merchant.dbaNameType: string
Required: Yes
Maximum length: 128 characters
The merchant's DBA/trading name.
The value is stored with the request and displayed to the Netevia reviewer.
It is not validated against the merchant record. Therefore, integrations should make sure the value is accurate because an incorrect DBA name can slow down manual review.
fundingDepartment
fundingDepartmentType: object
Required: Yes
Identifies the Reserve Funding department associated with the request.
fundingDepartment.departmentId
fundingDepartment.departmentIdType: string
Required: Yes
Maximum length: 64 characters
The Netevia-provided identifier of the Reserve department.
The ID must:
- belong to a live department;
- identify a Reserve department.
Risk departments cannot be used.
Possible validation errors include:
department not foundand:
Risk departments are not acceptedThe department's bank must also match the merchant's bank. This particular check is performed later during downstream processing rather than during request submission.
fundingDepartment.departmentName
fundingDepartment.departmentNameType: string
Required: Yes
Maximum length: 128 characters
The department name displayed with the request.
The name is stored as provided and is not used to resolve the department. The departmentId determines the actual department.
Funding Terms
The fundingTerms object defines how the reserve is collected.
"fundingTerms": {
"startDate": "2026-09-15",
"stopDate": "2026-12-31",
"percent": 10,
"amount": 500,
"ceiling": 25000
}At least one of percent or amount must be provided.
Both may be provided simultaneously.
fundingTerms.startDate
fundingTerms.startDateType: string
Format: ISO date or date-time
Required: Yes
The date on which the reserve begins accruing.
Example:
"startDate": "2026-09-15"If omitted, validation fails.
fundingTerms.stopDate
fundingTerms.stopDateType: string
Format: ISO date or date-time
Required: No
The date on which the reserve stops accruing.
For an open-ended reserve, omit the field entirely.
Example:
"startDate": "2026-09-15"An omitted stopDate means that the reserve remains open until the ceiling is reached.
If provided, stopDate must be on or after startDate.
Invalid example:
{
"startDate": "2026-12-31",
"stopDate": "2026-09-15"
}Response:
fundingTerms.stopDate must be on or after fundingTerms.startDate.fundingTerms.percent
fundingTerms.percentType: number
Required: At least one of percent or amount
The percentage of each settlement withheld for the reserve.
The value is a percentage, not a fraction.
Correct:
"percent": 10This means 10%, not 0.10%.
Rules
- Greater than
0 - Maximum
100 - Rounded to 4 decimal places before validation
Example:
"percent": 7.5means that 7.5% of each settlement is withheld.
fundingTerms.amount
fundingTerms.amountType: number
Required: At least one of percent or amount
A fixed amount withheld from each settlement.
Example:
"amount": 500means that $500 is withheld from each applicable settlement.
Rules
- Greater than
0 - Maximum
9,999,999.99 - Rounded to 4 decimal places
- At least one of
percentoramountmust be provided
Using percent and amount together
percent and amount togetherThe two fields are not mutually exclusive.
If both are provided, both deductions apply to each settlement.
For example:
{
"percent": 10,
"amount": 500
}For a $10,000 settlement:
- 10% =
$1,000 - fixed amount =
$500 - total withheld =
$1,500
fundingTerms.ceiling
fundingTerms.ceilingType: number
Required: Yes
The maximum total amount to be collected for the reserve.
Deductions stop when the ceiling is reached.
Example:
"ceiling": 25000Rules
- Greater than
0 - Maximum
9,999,999.99 - Rounded to 4 decimal places
The ceiling is required regardless of whether the reserve uses a percentage, a fixed amount, or both.
Document
The signed Reserve Funding agreement must be supplied as a document.
"document": {
"fileName": "Reserve-Agreement-The-Best-Tires.pdf",
"fileType": "pdf",
"fileUrl": "https://files.example.com/agreements/best-tires.pdf"
}Netevia downloads the document synchronously during the POST request.
Therefore, the URL must be accessible from Netevia's servers at submission time. An inaccessible document causes the request to fail and no Pending request is created.
document.fileName
document.fileNameType: string
Required: Yes
Maximum length: 256 characters
The name under which Netevia stores the document and the name displayed to the reviewer.
The filename extension determines the stored document type.
Supported extensions:
.pdf.jpg.jpeg.png
The following characters are not allowed:
" < > | : * ? / \For example:
Reserve-Agreement-Acme.pdfis valid.
A filename such as:
Reserve:Agreement.pdfis invalid.
The extension in fileName is authoritative. For example, a .docx filename is rejected even if fileType is set to pdf.
document.fileType
document.fileTypeType: string
Required: Yes
The declared document type.
Accepted values:
pdf
jpg
jpeg
pngThe value is case-insensitive and trimmed.
jpeg is accepted as an alias for jpg.
Example:
"fileType": "pdf"document.fileUrl
document.fileUrlType: string
Required: Yes
The URL from which Netevia retrieves the signed agreement.
The URL must be accessible from Netevia's servers without requiring an interactive login.
Example:
"fileUrl": "https://files.example.com/agreements/best-tires.pdf"If Netevia cannot download the file:
Document could not be retrieved from the provided URLis returned.
A URL that is only accessible inside the partner's private network will not work.
document.fileAccessToken
document.fileAccessTokenType: string
Required: No
Optional access token used when the document URL requires bearer authentication.
When provided, Netevia sends it with the document download request as:
Authorization: Bearer <fileAccessToken>For a publicly accessible document URL, omit this field.
email
emailType: string
Required: Yes
Maximum length: 256 characters
A contact address stored with the request and displayed to the Netevia reviewer.
The email format is not validated.
No email notification is sent to this address.
It is a contact/display field for the reviewer, not a notification mechanism.
Successful Response
HTTP 200
200A successfully created request returns HTTP 200.
Example:
{
"data": {
"requestId": "HQ-RF-REQ-101",
"externalRequestId": "RF-2026-09-0001",
"status": "Pending",
"duplicate": false
},
"requestId": 407166,
"success": true
}The response contains two different request identifiers:
| Field | Description |
|---|---|
data.requestId | Internal Reserve Funding request ID, e.g. HQ-RF-REQ-101 |
data.externalRequestId | Identifier supplied by the external service |
data.status | Current request status |
data.duplicate | Indicates whether the request was newly created |
requestId | ID of the HTTP API call in Netevia's API log |
success | Indicates successful processing |
Store data.requestId / internalRequestId for troubleshooting and correlation. The outer numeric requestId identifies the individual API call in Netevia's logs.
Duplicate Request
HTTP 200
200Submitting an already-used externalRequestId does not create another request.
Example:
{
"data": {
"requestId": "HQ-RF-REQ-101",
"externalRequestId": "RF-2026-09-0001",
"status": "Approved",
"duplicate": true
},
"requestId": 407170,
"success": true
}This should be treated as a successful idempotent retry.
The returned status is the current status of the existing request, not necessarily Pending.
Recommended retry logic
If the original POST times out:
- Retry the same POST using the same
externalRequestId. - If
duplicate = true, do not create another local request. - Use the returned status or call the GET endpoint to reconcile the current state.
Validation Errors
HTTP 400
400If validation fails, no request is created.
The API collects validation failures and returns them together.
Example:
{
"requestId": 407184,
"success": false,
"validationErrors": [
{
"field": "document.fileType",
"error": "document.fileType must be one of: pdf, jpg, png."
},
{
"field": "merchant.mid",
"error": "merchant not found"
},
{
"field": "fundingTerms",
"error": "At least one of fundingTerms.percent or fundingTerms.amount is required."
}
]
}The validation error property is named error, not message.
The field value normally uses the dotted path corresponding to the request body. Cross-field validation errors can be reported against the parent object, such as fundingTerms.
Common validation errors
| Error | Description |
|---|---|
merchant not found | The supplied MID does not exist |
department not found | The department ID does not identify a live department |
Risk departments are not accepted | The supplied department is not a Reserve department |
Ceiling is required and must be greater than 0. | Ceiling is missing, zero, negative, or invalid |
At least one of fundingTerms.percent or fundingTerms.amount is required. | Both deduction methods are missing |
fundingTerms.stopDate must be on or after fundingTerms.startDate. | Dates are in the wrong order |
document.fileType must be one of: pdf, jpg, png. | Unsupported declared document type |
document.fileName must have a pdf, jpg, jpeg or png extension. | Unsupported filename extension |
document.fileName contains characters that are not allowed in a file name. | Invalid filename |
Document could not be retrieved from the provided URL | Netevia could not download the document |
Request Lifecycle
A submitted request starts in:
PendingAfter manual review it becomes either:
Approvedor:
RejectedThese decisions are terminal.
A rejected request cannot be reopened or returned to Pending. If the request needs to be corrected and resubmitted, the external service must create a new request with a new externalRequestId.
2. Get Reserve Funding Request Status
GET /api/v1/reserve-funding/requests/{externalRequestId}Returns the current state of a previously submitted Reserve Funding request.
This endpoint is the authoritative reconciliation path.
It should be used:
- after submitting a request;
- after a network timeout;
- after receiving a duplicate response;
- when a callback is not received;
- periodically to reconcile request state.
Path Parameter
externalRequestId
externalRequestIdType: string
Required: Yes
The same identifier that was supplied when the request was created.
Example:
GET /api/v1/reserve-funding/requests/RF-2026-09-0001The value is trimmed before lookup.
The API user can only retrieve requests that were submitted by that same API user.
If the request belongs to another integrator, the API returns 404 rather than 403. This deliberately makes another integrator's requests indistinguishable from unknown requests.
If the identifier contains characters outside:
A-Z
a-z
0-9
-
_
.it should be URL-encoded.
Status Response
The response contains the current request state.
Possible statuses:
Pending
Approved
RejectedExample for a Pending request:
{
"data": {
"internalRequestId": "HQ-RF-REQ-101",
"externalRequestId": "RF-2026-09-0001",
"status": "Pending"
},
"requestId": 407200,
"success": true
}While the request is pending, decision-specific fields are absent, rather than returned as null.
Approved Request
Example:
{
"data": {
"internalRequestId": "HQ-RF-REQ-102",
"externalRequestId": "RF-2026-09-0002",
"status": "Approved",
"processedBy": "Maxim Rikhm",
"processedAt": "2026-09-11T12:08:49Z",
"processingResult": {
"status": "Success",
"errorCode": null,
"message": null
}
},
"requestId": 407201,
"success": true
}processedAt is returned in UTC using ISO-8601 format.
Rejected Request
Example:
{
"data": {
"internalRequestId": "HQ-RF-REQ-103",
"externalRequestId": "RF-2026-09-0003",
"status": "Rejected",
"processedBy": "Maxim Rikhm",
"processedAt": "2026-09-11T12:08:43Z",
"comment": "Signed agreement does not match the requested ceiling."
},
"requestId": 407202,
"success": true
}For a rejected request, comment contains the reviewer's rejection reason.
The reason is mandatory on the Netevia side and can contain up to 1024 characters.
processingResult
processingResultFor an approved request, processingResult describes whether Netevia successfully created the Reserve Funding Settings on the merchant profile.
Possible values:
Success
FailedSuccessful processing
"processingResult": {
"status": "Success",
"errorCode": null,
"message": null
}Failed processing
"processingResult": {
"status": "Failed",
"errorCode": "SETTINGS_CREATION_FAILED",
"message": "Failed to create Reserve Funding Settings in merchant profile."
}A critical distinction is that:
status = Approveddoes not by itself guarantee that the reserve was successfully created.
Immediately after approval, processingResult may be absent because downstream processing has not completed yet.
If:
status = Approved
processingResult.status = Failedthe approval decision remains valid, but the Reserve Funding Settings were not created. This requires investigation by Netevia.
HTTP 404 — Request Not Found
If the supplied externalRequestId cannot be found:
{
"requestId": 407185,
"success": false,
"error": "Reserve funding request not found."
}Possible causes include:
- incorrect
externalRequestId; - incorrect URL encoding;
- request was submitted using a different API user;
- request does not exist.
Recommended Integration Pattern
A reliable integration should use both the callback and the GET endpoint.
Initial submission
POST request
|
v
Pending
|
v
Netevia review
|
+---- Rejected ----> callback
|
+---- Approved ----> downstream processing
|
v
processingResult
|
v
callbackThe callback is useful for near-real-time notification, but the GET endpoint should always be considered the source of truth.
Recommended reconciliation logic
1. POST the request
Expect:
{
"data": {
"status": "Pending",
"duplicate": false
},
"success": true
}Store:
externalRequestIddata.requestId
2. Wait for the decision
The request remains Pending until a Netevia employee reviews it.
3. Process the callback
When a callback is received, update the local representation of the request.
4. Reconcile using GET
Regardless of whether a callback was received, periodically call:
GET /api/v1/reserve-funding/requests/{externalRequestId}This protects the integration against callback delivery failures.
5. Handle terminal states
For:
Rejectedstore the comment and stop processing.
For:
Approved + processingResult.status = Successconsider the reserve successfully created.
For:
Approved + processingResult.status = Failedkeep the approval decision but flag the request for Netevia investigation.
For:
Approvedwithout processingResult, continue polling because downstream processing may still be running.
Callback and GET Relationship
Netevia sends a callback after a decision is made.
For an approval, the callback is sent after downstream processing completes.
For a rejection, the callback is sent immediately after the request is rejected.
Example approval callback:
{
"externalRequestId": "RF-2026-09-0002",
"internalRequestId": "HQ-RF-REQ-102",
"status": "Approved",
"processedBy": "Maxim Rikhm",
"processedAt": "2026-09-11T12:08:49Z",
"comment": null,
"processingResult": {
"status": "Success",
"errorCode": null,
"message": null
}
}Example rejection callback:
{
"externalRequestId": "RF-2026-09-0003",
"internalRequestId": "HQ-RF-REQ-103",
"status": "Rejected",
"processedBy": "Maxim Rikhm",
"processedAt": "2026-09-11T12:08:43Z",
"comment": "Signed agreement does not match the requested ceiling."
}Callbacks are best-effort. Netevia makes two delivery attempts and does not roll back the decision if both attempts fail.
Therefore:
Do not use callback delivery as the source of truth. Use the GET endpoint for reconciliation.
Complete Example
Step 1 — Submit request
POST /api/v1/reserve-funding/requests
Authorization: Basic <credentials>
Content-Type: application/json
Accept: application/json{
"externalRequestId": "RF-2026-09-1029",
"merchant": {
"mid": "9180465590107730",
"dbaName": "The Best Tires"
},
"fundingDepartment": {
"departmentId": "STG824-RSV-9180",
"departmentName": "Reserve API Approval"
},
"fundingTerms": {
"startDate": "2026-09-15",
"stopDate": "2026-12-31",
"percent": 10,
"ceiling": 25000
},
"document": {
"fileName": "Reserve-Agreement-The-Best-Tires.pdf",
"fileType": "pdf",
"fileUrl": "https://files.example.com/agreements/best-tires.pdf"
},
"email": "[email protected]"
}Response:
{
"data": {
"requestId": "HQ-RF-REQ-101",
"externalRequestId": "RF-2026-09-1029",
"status": "Pending",
"duplicate": false
},
"requestId": 407166,
"success": true
}Step 2 — Check status
GET /api/v1/reserve-funding/requests/RF-2026-09-1029
Authorization: Basic <credentials>
Accept: application/jsonInitially:
{
"data": {
"internalRequestId": "HQ-RF-REQ-101",
"externalRequestId": "RF-2026-09-1029",
"status": "Pending"
},
"requestId": 407200,
"success": true
}Step 3 — Request approved
After manual review:
{
"data": {
"internalRequestId": "HQ-RF-REQ-101",
"externalRequestId": "RF-2026-09-1029",
"status": "Approved",
"processedBy": "Maxim Rikhm",
"processedAt": "2026-09-11T12:08:49Z",
"processingResult": {
"status": "Success",
"errorCode": null,
"message": null
}
},
"requestId": 407210,
"success": true
}At this point the Reserve Funding Settings have been successfully created.
Rejection and Resubmission
If a request is rejected, the rejection reason is returned in comment.
For example:
"comment": "Ceiling in the signed agreement is $30,000, while the request specifies $25,000."The external service should:
- Read the rejection reason.
- Correct the request or agreement.
- Submit a new request.
- Generate a new
externalRequestId.
Do not reuse the old externalRequestId.
Reusing the old identifier returns:
"duplicate": trueand does not create a new request.
Important Integration Rules
1. Use externalRequestId as the idempotency key
externalRequestId as the idempotency keyA network retry should reuse the same identifier.
Same externalRequestId
|
+--> Existing request returned
|
+--> duplicate = trueNever generate a new identifier merely because the original HTTP request timed out.
2. Do not treat Approved as equivalent to Success
Approved as equivalent to SuccessThese are two separate stages:
Approved
|
v
Reserve Funding Settings creation
|
v
processingResultTherefore:
Approved + no processingResultmeans processing is still pending.
Approved + Successmeans the reserve was created successfully.
Approved + Failedmeans the decision was approved but reserve creation failed.
3. Always keep externalRequestId and internalRequestId
externalRequestId and internalRequestIdThe two identifiers serve different purposes.
externalRequestId
externalRequestIdGenerated by the partner and used for:
- idempotency;
- GET requests;
- partner-side correlation.
internalRequestId
internalRequestIdGenerated by Netevia, for example:
HQ-RF-REQ-101Used for:
- Netevia-side correlation;
- troubleshooting;
- support requests;
- identifying the request in HQ.
The internal request ID returned by POST and the one returned by GET/callback remain the same.
4. The document must be accessible during POST
Netevia downloads the agreement as part of request processing.
Therefore, this will fail:
POST
|
+--> fileUrl
|
+--> private/internal URLUse a URL accessible from Netevia's servers.
If authentication is required, provide fileAccessToken.
5. Use a new externalRequestId after rejection
externalRequestId after rejectionRejected requests are terminal.
Request A
RF-2026-09-0001
|
v
Rejected
|
X
cannot reopen
|
v
Request B
RF-2026-09-0002Troubleshooting
When contacting Netevia about an API issue, provide either:
- the numeric
requestIdfrom the HTTP response; or - the internal request ID such as
HQ-RF-REQ-101.
The internal request ID identifies the Reserve Funding request, while the numeric requestId identifies the individual API call in Netevia's API logs.
Common cases
| Situation | Recommended action |
|---|---|
| POST timed out | Retry using the same externalRequestId |
POST returns duplicate: true | Do not create another request; reconcile using GET |
Request is Pending | Continue waiting/polling |
Request is Approved without processingResult | Continue polling |
Approved + processingResult.Success | Reserve successfully created |
Approved + processingResult.Failed | Contact Netevia with internalRequestId |
Request is Rejected | Read comment, correct the issue, submit a new request with a new externalRequestId |
GET returns 404 | Verify externalRequestId, URL encoding, and API user |
POST returns 400 | Fix the validation errors in validationErrors |
POST/GET returns 401 | Check API credentials and general API access |
POST/GET returns 403 | Request Reserve Funding API permission |
Summary
The recommended integration model is:
POST
|
v
Pending
|
Netevia review
/ \
/ \
Rejected Approved
| |
| v
| Reserve Settings
| processing
| |
| v
| processingResult
| |
+--------+-------+
|
v
Callback
|
v
GET reconciliationThe key integration principles are:
externalRequestIdis the idempotency key.- POST creation returns HTTP
200. - A successful POST creates a
Pendingrequest. Approvedand successful reserve creation are separate states.processingResultmay be temporarily absent after approval.- A rejected request cannot be reopened.
- Resubmission after rejection requires a new
externalRequestId. - The document must be downloadable by Netevia during the POST request.
- Callbacks are best-effort.
- GET is the authoritative reconciliation mechanism.
internalRequestIdshould be stored for Netevia support and troubleshooting.
