Open Finance Malaysia - Data Provider Documentation Portal

Download OpenAPI specification:Download

Overview

The PayNet Open Finance Platform (OFP) enables secure, user-consented sharing of financial data between institutions. As a Data Provider, your bank sits at the heart of this ecosystem, you are the source of truth for your customers' financial data.

Party Role
Data Consumer (DC) A third-party application (e.g. a PFM or lending platform) that requests access to a user's financial data
Data Provider (DP) Your bank, you authenticate the user, obtain their consent, and return their financial data when requested
PayNet OFP The intermediary platform that orchestrates consent, authentication, and secure data exchange between DC and DP

How data flows to your bank:

Unlike a DC, which calls PayNet's APIs, your bank primarily operates as a server that PayNet calls. PayNet OFP forwards requests to your endpoints (called webhooks) for actions like user authentication, consent notification, and data retrieval. Your responsibility is to implement these webhook endpoints correctly and respond according to the v1.2.1 specification.

PayNet OFP exposes two server components relevant to you:

  • Authorization Server (AS): manages the consent and authentication flow; your bank receives redirect webhooks and consent notifications from this component

  • Resource Server (RS): forwards data requests (accounts, balances, transactions) from DCs to your bank

Understanding the Journey

Before you start, you can simulate the User Experience as an End User starting from DC Simulator, Paynet Aggregator, and DP Simulator, for Consent Initiation up to Retrieve Balance Flow.

DP consent initiation screen

Run this Journey

  1. Access Paynet DC Simulator at https://dc-sim.uat.inet.ofm.my
  2. Start the Linking Account Process as per the UX Flow above
  3. DP Login credentials are:
    • Username: paynet
    • Password: paynet

Configuration & Pre-Requisite as a Data Provider

In this period, PayNet will support manual onboarding for Banks into PayNet Open Finance Platform.

Step 1: Submit Your Security Certificates

PayNet OFP enforces financial-grade security protocols. As a DP, you are required to upload certificates for the following two protocols.

Protocol Purpose
mTLS Mutual TLS, establishes authenticated, encrypted communication between your bank and PayNet OFP
JWS JSON Web Signature, signs your API responses so PayNet and DCs can verify the data has not been tampered with

How to Submit the Certificates:

Send email to PayNet xxxxxxxxx. (PayNet to prepare Instruction)

Step 2: Register Your API Base URLs

PayNet needs to know where to send requests. You must register the base URLs for both your Authorization Server and Resource Server endpoints.

How to Submit the API Base URL:

Send email to PayNet xxxxxxxxx. (PayNet to prepare Instruction)

What You Need to Build

User Interface Requirements

Your bank must provide a user-facing authorization interface, this is the login and consent approval page your customers see when they are redirected from a DC's application.

At minimum, this should include:

  • Login Page: where the user authenticates with their bank credentials
  • Account Selection Screen: where the user selects which accounts to share with the DC
  • Consent Review Screen: where the user reviews and either approves or rejects the data sharing request
  • Result Screen: confirmation or rejection message after the user makes their decision

Refer to the sample of UI mockup provided below for the expected screen flows as a Data Provider:

DP consent initiation screen

Mandatory Endpoints to Build

As a DP, your role is fundamentally different from a DC. Rather than calling APIs, you are building the APIs that PayNet will call. The table below summarises every endpoint your bank must implement.

  1. All Authorization Server Endpoints
  2. Resource Server Endpoints, for Drop 1:
    • Webhook Consent Event
    • Webhook Account Balance
    • Update Consent (DP call PayNet)

API Integration Guide

Serving Financial Data (Resource Webhooks)

Once a user has approved consent, a DC can request financial data through PayNet. PayNet forwards these requests to your bank's Resource Server webhooks. Your bank must retrieve the relevant data, encrypt it using the DC's JWE certificate, and return it wrapped inside a JWS-signed response.

Sequence Overview

DP Sequence Overview

Account Balances Webhook

POST https://{DP_URL}/v1/accounts/{account_id}/balances

PayNet forwards a balance request from a DC. Your bank must:

  1. Validate the incoming JWS signature on the request (signed by PayNet using your bank's cert kid)
  2. Look up the balance for the account_id
  3. Encrypt the balance data using the DC's encryption certificate (x-enc-kid)
  4. Return the encrypted payload wrapped in a JWS

Balance data to return:

Field Description
account_id The unique account identifier assigned during authorization
current_balance Real-time balance excluding pending transactions

🔒 This is the request from PayNet to your DP which must be made over mTLS using the certificate bound to your client_id.

curl --location --request GET \
  'https://{DP_URL}/v1/accounts/acc-550e8400-e29b-41d4-a716-446655440010/balances?consent_id=550e8400-e29b-41d4-a716-446655440001' \
  --cert /path/to/paynet-client.crt \
  --key /path/to/paynet-client.key \
  --cacert /path/to/ca.crt \
  --header 'x-fapi-interaction-id: 550e8400-e29b-41d4-a716-446655440000' \
  --header 'x-enc-kid: <base64url-SHA256-thumbprint-of-DC-cert>' \
  --header 'x-fapi-signature: eyJhbGciOiJQUzI1NiIsImtpZCI6IkFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2g....<JWS_COMPACT_SERIALIZATION>'

💡 The same request-validation and JWS-wrapped JWE response pattern applies to the Account and Account Transactions webhooks. Refer to the full DP API Spec v1.2.1 for the complete field definitions.

Testing Guide

Test Your DP Against Mock Data Consumer

This is your primary integration test which will be initiated from Mock Data Consumer.

Pre-work Checklist

Before signalling readiness to test, confirm the following:

  • All prerequisites steps are complete (certificates uploaded, base URLs registered)
  • All mandatory webhook endpoints are implemented and accessible
  • Your authorization UI (login, account selection, consent approval) is functional
  • Your endpoints are protected by mTLS and return correctly signed/encrypted responses

Backend Initiated Test

  1. Inform PayNet that your DP implementation is ready for testing. PayNet will coordinate directly with you to initiate the test.
  2. PayNet will share a PyTest suite to initiate DC call with mock DC credentials setup
  3. Update the API Base URL and Certificates to direct PayNet OFP calls into your DP

End-to-End Test Journey

The following table describes each step of the test, which party drives it, and the expected outcomes as per UI, Backend API, and API Integration Guide in the previous sections. You can focus on the bold rows for your DP testing purpose.

Step Actor Action UI Expected Outcome DP Backend Expected Outcome
1 Mock DC User navigates to the account linking screen Linking screen displayed -
2 Mock DC User selects a Data Provider from the list DP selection -
3 Mock DC User reviews consent details on DC UI Consent review screen displayed as per request -
4 Mock DC User confirms and submits consent User is redirected to Mock DP UI -
5 Real DP User is redirected to the Mock DP login page Mock DP login screen displayed Expose: /consents/events and /authorize
6a Real DP User logs in with valid preset credentials (positive) Login succeeds; account selection screen displayed DP internal implementation
6b Real DP User enters incorrect password (negative) Login fails; error message displayed DP internal implementation
7 Real DP User selects one or more accounts to link Accounts selected DP internal implementation
8 Real DP User reviews consent at the DP side Consent review screen displayed DP internal implementation
9a Real DP User approves consent (positive) User redirected to DP success page Call Update Consent consents/{consent_id} with positive status
9b Real DP User rejects consent (negative) User redirected back to DC with rejection error Call Update Consent consents/{consent_id} with negative status
10 Real DP User gets Success confirmation and click Back to DC User redirected back to DC with authorization_code -
11a Mock DC Consent approved callback received DC retrieves and displays account balance Expose /balance
11b Mock DC Consent rejected callback received DC displays "Account not linked" message to user -

Definition of Done

Your build is considered complete for this phase when all of the following have been demonstrated with your Certificates and Base URL setup:

  • Consent handled successfully: Your DP exposes the Consent Event endpoint (POST /v1/consents/events), validates the incoming JWS signature, and returns the correct response
  • User authenticated properly: Your DP exposes the Authorize endpoint (GET /v1/oauth/authorize), and your end user is correctly redirected through the DP authentication flow through to the point of consent approval or rejection
  • PayNet updated: Your DP calls Update Consent (PATCH /v1/consents/{consent_id}) to PayNet appropriately, with a positive status on approval and a negative status on rejection
  • Resource API functional: Your DP's Get Balance endpoint (GET /v1/accounts/{account_id}/balances) is functional and returns the correct encrypted and signed response

Test Your DP Against a Real Data Consumer

This phase verifies interoperability between your bank and a live DC in a controlled bilateral pairing session.

⚠️ Prerequisite: Your bank must have passed all tests as a DP before this phase can begin.

How to proceed:

Contact PayNet to schedule a bilateral testing session with a Real DC. PayNet will identify a suitable DC partner, facilitate the pairing, and provide guidance on the specific scenarios to execute together.

Error Codes

HTTP Status Codes

Code Description
200 OK
201 Created
204 No Content
303 Redirect
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
405 Method Not Allowed
429 Too Many Requests
500 Internal Server Error
503 Service Unavailable

OAuth 2.0 Error Codes

Returned in error responses from authorization and token endpoints.

Code Description
invalid_request Missing or invalid parameter.
invalid_client Client authentication failed.
invalid_grant Invalid, expired, or revoked grant/code.
unauthorized_client Client not authorized for this grant type.
unsupported_grant_type Grant type not supported.
access_denied Resource owner or AS denied the request.
invalid_scope Invalid or unknown scope.
server_error Unexpected server-side condition.
temporarily_unavailable Server overloaded or in maintenance.

Account & Application Error Codes

Code Description
Consent.AccountTemporarilyBlocked Account temporarily blocked by DP business rule.
Consent.PermanentAccountAccessFailure Account permanently unavailable.
Consent.TransientAccountAccessFailure Account unavailable due to transient failure.
AccessToken.InvalidScope Token does not have required scope.
Consent.Invalid Consent is in an invalid state.
Consent.BusinessRuleViolation DP business rule prevents the operation.
JWS.InvalidSignature JWS signature verification failed.
JWS.InvalidClaim One or more JWS claims failed validation.
JWE.DecryptionError JWE decryption failure.

Error Response Format

Error Response Body

{
  "error": "invalid_grant",
  "error_description": "The authorization code has expired."
}

Authorization Server

Webhook – Authorization

Invoked by PN (PayNet) to obtain authorization; DP responds with 303 redirect.

query Parameters
consent_id
required
string <= 36 characters

Unique identification as assigned to identify the consent.

redirect_uri
required
string <= 300 characters

PN redirection URI to which the response will be sent.

signature
required
string

JWS message signing (JWS) signed by PN private key with consent_id as the jws-payload.

Responses

JSON Web Key Set (PayNet)

Returns the JSON Web Key Set for PayNet.

Responses

Response samples

Content type
application/json
{
  • "keys": [
    ]
}

JSON Web Key Set (DC)

Returns the JSON Web Key Set for the given Distribution Partner (DC).

path Parameters
dc_id
required
string

Distribution Partner (DC) identifier

Responses

Response samples

Content type
application/json
{
  • "keys": [
    ]
}

Webhook – User Info

Invoked by the AS to retrieve end-user info.

query Parameters
consent_id
required
string

Unique identification as assigned to identify the consent.

header Parameters
x-fapi-signature
required
string

JWS message signing (JWS) compact serialization detached signed by sender's private key.

x-fapi-interaction-id
required
string <uuid>

An UUID used as a correlation id.

Responses

Response samples

Content type
application/json
{
  • "sub": "string",
  • "name": "string",
  • "given_name": "string",
  • "family_name": "string",
  • "preferred_username": "string",
  • "picture": "string",
  • "email": "string",
  • "email_verified": true,
  • "id_type": "nric",
  • "hashed_id_number": "string"
}

Resource Server

Account Balances

PayNet retrieves an account balances information from DP.

path Parameters
account_id
required
string

AccountID is the account identifier (path).

Responses

Response samples

Content type
application/json
{
  • "status": "string",
  • "error_message": "string",
  • "data": "string"
}

Consent Event

Description: PayNet notifies DP when consent is created or updated.

Request Body schema: application/json
event_type
string
Default: "consent_event_type_unspecified"
Enum: "consent_event_type_unspecified" "consent_created" "consent_updated" "consent_status_updated"

EventType is the consent event type (PayNet section 13).

object

Data is the consent object.

Responses

Request samples

Content type
application/json
{
  • "event_type": "consent_event_type_unspecified",
  • "data": {
    }
}

Response samples

Content type
application/json
{ }

Update Consent

DP updates consent status and user info after the user authorizes the consent.

path Parameters
consent_id
required
string

ConsentID is the consent identifier (from path parameter).

Request Body schema: application/json
consent_id
string

ConsentID is the consent identifier (from path parameter).

status
string

Status is the consent status (32 chars, required).

id_token
string

IDToken is the JWT issued by the Data Provider (3000 chars, required if status = Authorized).

object

UserIdentity is the user identity object (required if status = Authorized).

Array of objects

Accounts is the list of consent account objects (required if status = Authorized).

Responses

Request samples

Content type
application/json
{
  • "consent_id": "string",
  • "status": "string",
  • "id_token": "string",
  • "user_identity": {
    },
  • "accounts": [
    ]
}

Response samples

Content type
application/json
{ }