ReserveFundingApi

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:

MethodEndpointDescription
POST/api/v1/reserve-funding/requestsSubmit 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.dev

Production 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/json

The API user must have:

  1. General Netevia API access.
  2. Reserve Funding API access permission.

Without general API access, the API returns 401.

Without the Reserve Funding API permission, the API returns 403.

Authentication errors

HTTP statusErrorDescription
401Authentication failureInvalid credentials, unknown user, or missing authentication header
401Using of API is not allowedThe user is valid but API access is disabled or restricted
403The 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:

  1. The merchant signs the Reserve Funding agreement.
  2. The external service submits the request using POST.
  3. Netevia HQ validates the request and downloads the signed document.
  4. A request is created in Netevia HQ with status Pending.
  5. A Netevia employee reviews the request.
  6. The employee either:
    • approves the request, or
    • rejects the request with a reason.
  7. For an approved request, Netevia creates the Reserve Funding Settings on the merchant profile.
  8. Netevia sends a decision callback to the external service.
  9. The external service can use the GET endpoint 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/requests

Submits 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

Type: 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 GET endpoint.

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": true

The 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.

merchant

Type: object
Required: Yes

Contains information about the merchant for which the reserve will be created.

merchant.mid

Type: 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 found

is returned as a validation error.

merchant.dbaName

Type: 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

Type: object
Required: Yes

Identifies the Reserve Funding department associated with the request.

fundingDepartment.departmentId

Type: 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 found

and:

Risk departments are not accepted

The 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

Type: 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

Type: 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

Type: 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

Type: 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": 10

This means 10%, not 0.10%.

Rules

  • Greater than 0
  • Maximum 100
  • Rounded to 4 decimal places before validation

Example:

"percent": 7.5

means that 7.5% of each settlement is withheld.


fundingTerms.amount

Type: number
Required: At least one of percent or amount

A fixed amount withheld from each settlement.

Example:

"amount": 500

means 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 percent or amount must be provided

Using percent and amount together

The 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

Type: number
Required: Yes

The maximum total amount to be collected for the reserve.

Deductions stop when the ceiling is reached.

Example:

"ceiling": 25000

Rules

  • 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

Type: 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.pdf

is valid.

A filename such as:

Reserve:Agreement.pdf

is invalid.

The extension in fileName is authoritative. For example, a .docx filename is rejected even if fileType is set to pdf.


document.fileType

Type: string
Required: Yes

The declared document type.

Accepted values:

pdf
jpg
jpeg
png

The value is case-insensitive and trimmed.

jpeg is accepted as an alias for jpg.

Example:

"fileType": "pdf"


document.fileUrl

Type: 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 URL

is returned.

A URL that is only accessible inside the partner's private network will not work.


document.fileAccessToken

Type: 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

Type: 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

A 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:

FieldDescription
data.requestIdInternal Reserve Funding request ID, e.g. HQ-RF-REQ-101
data.externalRequestIdIdentifier supplied by the external service
data.statusCurrent request status
data.duplicateIndicates whether the request was newly created
requestIdID of the HTTP API call in Netevia's API log
successIndicates 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

Submitting 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:

  1. Retry the same POST using the same externalRequestId.
  2. If duplicate = true, do not create another local request.
  3. Use the returned status or call the GET endpoint to reconcile the current state.

Validation Errors

HTTP 400

If 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

ErrorDescription
merchant not foundThe supplied MID does not exist
department not foundThe department ID does not identify a live department
Risk departments are not acceptedThe 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 URLNetevia could not download the document


Request Lifecycle

A submitted request starts in:

Pending

After manual review it becomes either:

Approved

or:

Rejected

These 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

Type: string
Required: Yes

The same identifier that was supplied when the request was created.

Example:

GET /api/v1/reserve-funding/requests/RF-2026-09-0001

The 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
Rejected

Example 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

For an approved request, processingResult describes whether Netevia successfully created the Reserve Funding Settings on the merchant profile.

Possible values:

Success
Failed

Successful 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 = Approved

does 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 = Failed

the 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
                           callback

The 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:

  • externalRequestId
  • data.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:

Rejected

store the comment and stop processing.

For:

Approved + processingResult.status = Success

consider the reserve successfully created.

For:

Approved + processingResult.status = Failed

keep the approval decision but flag the request for Netevia investigation.

For:

Approved

without 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/json

Initially:

{
  "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:

  1. Read the rejection reason.
  2. Correct the request or agreement.
  3. Submit a new request.
  4. Generate a new externalRequestId.

Do not reuse the old externalRequestId.

Reusing the old identifier returns:

"duplicate": true

and does not create a new request.


Important Integration Rules

1. Use externalRequestId as the idempotency key

A network retry should reuse the same identifier.

Same externalRequestId
        |
        +--> Existing request returned
        |
        +--> duplicate = true

Never generate a new identifier merely because the original HTTP request timed out.


2. Do not treat Approved as equivalent to Success

These are two separate stages:

Approved
   |
   v
Reserve Funding Settings creation
   |
   v
processingResult

Therefore:

Approved + no processingResult

means processing is still pending.

Approved + Success

means the reserve was created successfully.

Approved + Failed

means the decision was approved but reserve creation failed.


3. Always keep externalRequestId and internalRequestId

The two identifiers serve different purposes.

externalRequestId

Generated by the partner and used for:

  • idempotency;
  • GET requests;
  • partner-side correlation.

internalRequestId

Generated by Netevia, for example:

HQ-RF-REQ-101

Used 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 URL

Use a URL accessible from Netevia's servers.

If authentication is required, provide fileAccessToken.


5. Use a new externalRequestId after rejection

Rejected requests are terminal.

Request A
RF-2026-09-0001
        |
        v
    Rejected
        |
        X
   cannot reopen
        |
        v
Request B
RF-2026-09-0002

Troubleshooting

When contacting Netevia about an API issue, provide either:

  • the numeric requestId from 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

SituationRecommended action
POST timed outRetry using the same externalRequestId
POST returns duplicate: trueDo not create another request; reconcile using GET
Request is PendingContinue waiting/polling
Request is Approved without processingResultContinue polling
Approved + processingResult.SuccessReserve successfully created
Approved + processingResult.FailedContact Netevia with internalRequestId
Request is RejectedRead comment, correct the issue, submit a new request with a new externalRequestId
GET returns 404Verify externalRequestId, URL encoding, and API user
POST returns 400Fix the validation errors in validationErrors
POST/GET returns 401Check API credentials and general API access
POST/GET returns 403Request 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 reconciliation

The key integration principles are:

  1. externalRequestId is the idempotency key.
  2. POST creation returns HTTP 200.
  3. A successful POST creates a Pending request.
  4. Approved and successful reserve creation are separate states.
  5. processingResult may be temporarily absent after approval.
  6. A rejected request cannot be reopened.
  7. Resubmission after rejection requires a new externalRequestId.
  8. The document must be downloadable by Netevia during the POST request.
  9. Callbacks are best-effort.
  10. GET is the authoritative reconciliation mechanism.
  11. internalRequestId should be stored for Netevia support and troubleshooting.