Lewati ke konten utama

Kesles Merchant Partner API Reference

This document summarizes the Kesles Merchant APIs that are relevant to partner cooperation needs for accessing merchant data, transactions, dashboards, and reference data.

This document is not a new partner API contract. Its content reads from the backend implementation already active in:

  • merchant_core_api

Goals of this document:

  • give an overview of the endpoints already available
  • explain the authentication patterns currently in place
  • distinguish between endpoints that are safe for partner integration and endpoints that are still internal-only
  • provide a basis for discussion if a more formal partner API is created later

Status Summary

Kesles Merchant already has the initial foundation of a partner namespace:

  • /api/partner/v1/...

The implementation has since progressed significantly — a formal /api/partner/v1 namespace is now active (implemented in partner_service on port 8086 and reverse-proxied through merchant_core_api) including referral portfolio and commission reporting. What is currently available falls into four large groups:

  1. public endpoint
  2. bearer endpoint for the merchant user/app/dashboard
  3. internal endpoint for internal service/operator needs
  4. partner endpoint read-only for transactions, merchant referrals, and scoring

This means that for current partner cooperation there are three realistic approaches:

  1. partner accesses data on behalf of a merchant user using a bearer token
  2. partner is whitelisted as a server-to-server internal integration using X-Internal-API-Key
  3. partner uses the /api/partner/v1 endpoints with credential_key and credential_secret

The formal partner contract that is now starting to be prepared uses the pattern:

  • partner_id
  • credential_key
  • credential_secret
  • per-partner audit log
  • per-partner rate limit
  • partner-specific endpoint whitelist

This document focuses on the APIs that already exist today, including the early implementation of /api/partner/v1.

Base URL

Common base URLs in use today:

  • local: http://localhost:8080
  • production: https://kesles.com/merchant/api

Authentication Types

1. Public

No token required.

Used for:

  • legal documents
  • UMKM academy
  • some reference lookups

2. Bearer Token

Use the header:

Authorization: Bearer <access_token>

This pattern is used when the partner acts on behalf of a merchant/user.

Endpoints relevant for merchant data:

  • GET /merchant/home-dashboard
  • GET /merchant/transactions
  • POST /merchant/registration
  • GET /merchant/registration/device-offer
  • POST /merchant/feedback
  • GET /auth/profile
  • PUT /auth/profile
  • GET /auth/account-settings
  • PUT /auth/account-settings

3. Internal API Key

Use the header:

X-Internal-API-Key: <INTERNAL_NOTIFICATION_API_KEY>

These endpoints are not recommended to be used directly by general external partners, because their scope is still internal-operational.

Internal endpoints currently active:

  • POST /internal/notifications/whatsapp/otp
  • POST /internal/notifications/push/test
  • POST /internal/merchant/registration/review
  • POST /internal/merchant/registration/approve
  • POST /internal/merchant/status

4. Partner Credential

Use the endpoint:

POST /api/partner/v1/auth/token

Request:

{
"credential_key": "partner-demo-key",
"credential_secret": "dev-secret-partner-demo"
}

Response:

{
"token_type": "Bearer",
"access_token": "<jwt>",
"expires_in_secs": 900,
"scope": [
"merchant.read",
"transaction.read",
"reference.read"
],
"partner_id": "uuid",
"partner_code": "PRT-SALES-001",
"partner_name": "PT Sales Perdana",
"credential_name": "Sales Dev Credential"
}

Demo development credentials prepared today:

  • partner-demo-key / dev-secret-partner-demo — PT Sales Perdana (sales, scope: merchant.read, transaction.read)
  • finance-demo-key / dev-secret-finance-demo — Bank Mandiri (bank, scope: merchant.read, scoring.read)
  • vc-demo-key / dev-secret-vc-demo — East Ventures (venture_capital, scope: merchant.read only)

Common Error Format

Most JSON errors use the pattern:

{
"error": "human readable message",
"code": "machine_readable_code",
"retry_after_secs": 60
}

Notes:

  • retry_after_secs is not always present
  • code should be used by partners for handling logic
  • error is more suitable for logging or debugging

Before opening transaction data access to partners, the partner model must be clearly distinguished. The current document is still generic; from a business perspective it makes more sense to split partners into at least two groups:

1. Sales / referral partners

This kind of partner may not see all merchants. The safest scope is only merchants that:

  • are referred by that partner, or
  • have a referral_code pointing to the related sales partner

Implications:

  • transaction access must be restricted per partner
  • partner must not be able to read merchants outside its referral portfolio
  • ideally there is a mapping table such as:
    • partner_id
    • merchant_id
    • referral_code
    • access_status

Data typically sufficient for sales partners:

  • merchant identity
  • merchant status
  • onboarding/approval status
  • summary of referral merchant transactions
  • referral merchant performance

2. Banking / finance / scoring partners

This kind of partner may access transaction data only for business scoring purposes, not to freely view all merchant data.

This means their access should:

  • be based on merchant consent or a formal cooperation agreement
  • be limited to fields relevant to underwriting/scoring
  • not expose sensitive data that is not needed

Examples of data relevant to scoring:

  • daily/weekly/monthly revenue
  • transaction count
  • average ticket size
  • transaction growth trend
  • merchant status

Examples of data that should be restricted or masked:

  • user NIK
  • full account number
  • raw payer reference if not needed
  • personal identity details that are not relevant

A. If a partner needs to read merchant data of a specific user

Use the flow:

  1. POST /auth/resolve-phone
  2. POST /auth/request-otp
  3. POST /auth/verify-otp
  4. store access_token
  5. access merchant endpoints via bearer token

This pattern is suitable when the partner:

  • is embedded in the merchant flow
  • works with the merchant's consent
  • needs access to the dashboard and transactions of the related merchant

Important notes:

  • the partner must not get terminal device control access
  • the partner must not get terminal pairing access
  • the partner must not get access to read merchant terminal details

B. If a partner only needs master/reference data

Use public/reference endpoints without bearer when possible.

C. If a partner needs operational server-to-server integration

There is currently no dedicated partner API. If this still has to be done, it usually requires:

  • whitelisting use of internal APIs
  • a security agreement
  • additional audit log
  • IP / API key restrictions

For sales partners and finance partners, I recommend not using internal endpoints directly. Both are better served by formal partner endpoints with different scope and payloads.

Existing Endpoints

/api/partner/v1 Endpoints Already Implemented

The following endpoints are now available in the backend (implemented in partner_service):

  1. POST /api/partner/v1/auth/token
  2. GET /api/partner/v1/partners/{partner_id}/referral-merchants
  3. GET /api/partner/v1/partners/{partner_id}/referral-merchants/summary
  4. GET /api/partner/v1/partners/{partner_id}/commission-basis
  5. GET /api/partner/v1/partners/{partner_id}/settlements
  6. GET /api/partner/v1/partners/{partner_id}/portfolio
  7. GET /api/partner/v1/merchants/{merchant_id}
  8. GET /api/partner/v1/merchants/{merchant_id}/transactions
  9. GET /api/partner/v1/merchants/{merchant_id}/scoring-summary
  10. GET /api/partner/v1/merchants/{merchant_id}/dashboard
  11. POST /api/partner/v1/merchant-registrations
  12. GET /api/partner/v1/merchant-registrations/device-offer
  13. GET /api/partner/v1/references/*

Access rules:

  • partner can only read merchants that fall within the referral scope, access mapping, or consent
  • partner does not get access to terminals, pairings, or device control
  • the scoring endpoint is intended for finance/scoring partners, not for unrestricted access to all merchant data

POST /api/partner/v1/auth/token

  • Auth: bearer not required, uses credential_key and credential_secret
  • Function: issues a partner access token

GET /api/partner/v1/partners/{partner_id}/referral-merchants

  • Auth: partner bearer
  • Scope: merchant.read
  • Function: list of referral merchants belonging to the partner

GET /api/partner/v1/partners/{partner_id}/referral-merchants/summary

  • Auth: partner bearer
  • Scope: merchant.read
  • Function: aggregate of referral merchants and their transaction performance

GET /api/partner/v1/merchants/{merchant_id}/transactions

  • Auth: partner bearer
  • Scope: transaction.read
  • Function: merchant transactions within the partner's scope
  • Supported queries:
    • period
    • anchor_date

GET /api/partner/v1/merchants/{merchant_id}/scoring-summary

  • Auth: partner bearer
  • Scope: scoring.read or risk-summary.read
  • Function: business scoring summary based on transactions

Main fields:

  • average_monthly_gross_amount
  • average_monthly_transaction_count
  • avg_ticket_size
  • growth_30d
  • growth_90d
  • settlement_consistency_score
  • transaction_stability_score

GET /api/partner/v1/partners/{partner_id}/commission-basis

  • Auth: partner bearer
  • Scope: transaction.read
  • Function: commission breakdown per revenue-share rule for a billing period
  • Supported queries:
    • periodYYYY-MM format, default = current calendar month

Response:

{
"schema_version": "1",
"partner_id": "uuid",
"period": "2026-05",
"period_start_at": "2026-05-01T00:00:00Z",
"period_end_at": "2026-06-01T00:00:00Z",
"currency": "IDR",
"total_commission_amount": 1500000,
"rules_matched": 2,
"rows": [
{
"rule_id": "uuid",
"fee_type": "qris_mdr",
"basis": "gross_amount",
"rate_bps": 30,
"fixed_amount": null,
"source_partner_id": "uuid",
"source_partner_name": "PT Sales Perdana",
"merchant_count": 150,
"gross_amount": 5000000000,
"net_amount": 4850000000,
"transaction_count": 2500,
"commission_amount": 1500000
}
]
}

Error: 400 bad_request if period is not in YYYY-MM format.


GET /api/partner/v1/partners/{partner_id}/portfolio

  • Auth: partner bearer
  • Scope: merchant.read
  • Function: partner-wide rollup for the last 30 days — totals, tier distribution
  • No query parameters

Response:

{
"schema_version": "1",
"partner_id": "uuid",
"window": {
"days": 30,
"computed": "materialized_view",
"currency": "IDR"
},
"totals": {
"merchant_count": 500,
"active_merchant_count": 380,
"inactive_merchant_count": 90,
"churned_merchant_count": 30,
"new_30d": 25,
"gross_amount_30d": 15000000000,
"net_amount_30d": 14500000000,
"transaction_count_30d": 45000
},
"tiers": [
{ "grade": "A", "count": 50, "gross_share": 0.45 },
{ "grade": "B", "count": 150, "gross_share": 0.35 },
{ "grade": "C", "count": 180, "gross_share": 0.20 }
]
}

Note: tiers[].gross_share is a decimal fraction (0–1), not a percentage.


1. Health & Observability

GET /health

  • Auth: not required
  • Purpose: health check of the main service

Example response:

{
"service": "merchant_core_api",
"status": "ok",
"db_reference_status": "connected"
}

GET /health/otp-runtime

  • Auth: not required
  • Purpose: check the active OTP runtime store

Example response:

{
"service": "merchant_core_api",
"status": "ok",
"otp_runtime_store": "redis"
}

GET /metrics

  • Auth: not required
  • Purpose: observability/metrics
  • Note: more suitable for internal monitoring, not for business partner consumption

2. Auth & Session

These endpoints are important if the partner needs to obtain a bearer token to access merchant data.

POST /auth/resolve-phone

  • Auth: not required
  • Function:
    • check the phone number
    • determine whether OTP is needed
    • or authenticate directly if the device is already known

Request body:

{
"phone": "08114169868",
"platform_code": "mobile_user",
"device_id": "android-123",
"device_name": "Samsung A54"
}

Possible responses:

{
"status": "verification_required",
"phone": "08114169868",
"reason": "new_device",
"verification_channel": "whatsapp"
}

or:

{
"status": "authenticated",
"phone": "08114169868",
"token_type": "Bearer",
"access_token": "jwt-access-token",
"refresh_token": "jwt-refresh-token",
"expires_in_secs": 3600
}

POST /auth/request-otp

  • Auth: not required
  • Function: send OTP to the user's phone number

Request body:

{
"phone": "08114169868",
"method": "whatsapp",
"platform_code": "mobile_user",
"device_id": "android-123",
"device_name": "Samsung A54"
}

Example response:

{
"status": "otp_sent",
"request_id": "uuid",
"phone": "08114169868",
"method": "whatsapp",
"expires_in_secs": 300,
"retry_after_secs": 60
}

POST /auth/verify-otp

  • Auth: not required
  • Function: verify the OTP and issue an access token

Request body:

{
"request_id": "uuid",
"phone": "08114169868",
"code": "123456",
"platform_code": "mobile_user",
"device_id": "android-123",
"device_name": "Samsung A54"
}

Example response:

{
"status": "verified",
"phone": "08114169868",
"verified": true,
"token_type": "Bearer",
"access_token": "jwt-access-token",
"refresh_token": "jwt-refresh-token",
"expires_in_secs": 3600
}

GET /auth/device-login/cancel

  • Auth: not required
  • Required query:
    • token
  • Function: cancel a new-device login

POST /auth/push-tokens

  • Auth: bearer
  • Function: register the device push token

Request body:

{
"push_token": "firebase-token",
"device_id": "android-123",
"device_name": "Samsung A54"
}

Response:

{
"status": "registered"
}

3. Profile & Account Settings

These endpoints are appropriate when the partner needs to read or update the profile of the currently logged-in merchant user.

GET /auth/profile

  • Auth: bearer
  • Function: read the current user's profile

Common data in the response:

  • user_id
  • phone
  • email
  • full_name
  • nik
  • gender
  • address_line
  • avatar_url
  • merchant_status
  • email/phone verification status

PUT /auth/profile

  • Auth: bearer
  • Function: update the user profile

Request fields supported by the current implementation:

  • full_name
  • nik
  • gender
  • address_line
  • avatar_url
  • email

Notes:

  • if the email is already used by another user, the backend may return 409 Conflict

GET /auth/account-settings

  • Auth: bearer
  • Function: read the user's account settings

Response fields:

  • notification_push_enabled
  • notification_transactions_enabled
  • notification_promotions_enabled
  • notification_daily_summary_enabled
  • app_language
  • privacy_analytics_enabled
  • privacy_data_sharing_enabled
  • updated_at

PUT /auth/account-settings

  • Auth: bearer
  • Function: update account settings

Example request:

{
"notification_push_enabled": true,
"notification_transactions_enabled": true,
"notification_promotions_enabled": false,
"notification_daily_summary_enabled": true,
"app_language": "id",
"privacy_analytics_enabled": true,
"privacy_data_sharing_enabled": false
}

POST /auth/profile/email/request-otp

  • Auth: bearer
  • Function: request OTP for email verification/update

Request body:

{
"email": "merchant@example.com"
}

POST /auth/profile/email/verify-otp

  • Auth: bearer
  • Function: verify the email OTP

Request body:

{
"email": "merchant@example.com",
"code": "123456"
}

4. Merchant Registration

These endpoints are suitable for partners helping with the new merchant onboarding process.

GET /merchant/registration/device-offer

  • Auth: bearer
  • Function: fetch the active device offer for merchant registration

Example response:

{
"offer_code": "QRIS-PLUS-STD",
"terminal_type_code": "qris_plus",
"terminal_type_name": "QRIS Plus",
"device_type_name": "EDC Android",
"device_name": "Sunmi P2",
"device_price_amount": 2500000,
"shipping_fee_amount": 100000,
"promo_amount": 500000
}

POST /merchant/registration

  • Auth: bearer
  • Function: submit a new merchant application

Important request fields from the current implementation:

  • merchant_name
  • business_entity_type
  • business_type_id
  • average_monthly_revenue
  • employee_count
  • contact_phone
  • contact_email
  • address_line
  • country_id
  • country_name
  • province_id
  • province_name
  • city_id
  • city_name
  • district_id
  • district_name
  • subdistrict_id
  • subdistrict_name
  • postal_code_id
  • postal_code
  • bank_name
  • account_number
  • account_holder_name
  • device_offer_code
  • device_quantity
  • device_price_amount
  • shipping_fee_amount
  • promo_amount
  • device_fee_service_accepted
  • terms_of_service_accepted
  • privacy_policy_accepted
  • referral_code

Important validation notes:

  • business_entity_type must be Perseorangan (Individual) or Badan Usaha (Business Entity)
  • bank_name is validated against the PSP settlement bank reference
  • device_offer_code is validated against active offers
  • device fee values are checked against the active offer
  • if the user already has an active merchant or an open registration, the backend may return 409 Conflict

Successful response:

  • status pending
  • merchant object
  • the latest user profile

POST /merchant/feedback

  • Auth: bearer
  • Function: send merchant feedback
  • Suitable for operational needs or in-app feedback

5. Merchant Data Access

These are the endpoints most relevant when a partner needs to read operational merchant data.

GET /merchant/home-dashboard

  • Auth: bearer
  • Function: fetch a summary of the merchant dashboard data

The response contains:

  • merchant_id
  • merchant_name
  • merchant_status
  • balance_total
  • today_summary
    • gross_amount
    • mdr_fee_amount
    • service_fee_amount
    • net_amount
    • transaction_count
  • recent_transactions
  • weekly_gross_points
  • weekly_reference_date

Example partner use cases:

  • embedded dashboard
  • partner analytics panel
  • partner monitoring of merchant revenue

GET /merchant/transactions

  • Auth: bearer
  • Function: read the merchant transaction list

Supported query parameters:

  • period
  • anchor_date

Notes:

  • the limit is currently hard-coded in the backend to 50

The response contains:

  • merchant_id
  • merchant_name
  • merchant_status
  • period
  • period_start_at
  • period_end_at
  • summary
  • items

Important fields per item:

  • id
  • transaction_code
  • external_reference
  • transaction_status
  • payment_method
  • payment_channel
  • device_id
  • nmid
  • gross_amount
  • mdr_rate
  • mdr_fee_amount
  • net_amount_after_mdr
  • daily_service_fee_amount
  • transaction_at
  • settled_at
  • payer_reference
  • notes

6. Reference Data

These endpoints are appropriate for partners needing to display merchant onboarding master data.

GET /references/provinces

  • Auth: not required
  • Response:
{
"items": [
{
"id": "uuid",
"code": "73",
"name": "Sulawesi Selatan"
}
]
}

GET /references/cities?province_id=<id>

  • Auth: not required
  • Required query:
    • province_id

GET /references/districts?city_id=<id>

  • Auth: not required
  • Required query:
    • city_id

GET /references/subdistricts?district_id=<id>

  • Auth: not required
  • Required query:
    • district_id

GET /references/postal-codes?subdistrict_id=<id>

  • Auth: not required
  • Required query:
    • subdistrict_id

GET /references/business-scales

  • Auth: not required
  • Function: list of business scales

Response fields:

  • id
  • code
  • name
  • modal_usaha_min
  • modal_usaha_max
  • omzet_per_bulan_min
  • omzet_per_bulan_max
  • jumlah_karyawan_min
  • jumlah_karyawan_max
  • sort_order

GET /references/business-types

  • Auth: not required
  • Function: list of business types

Response fields:

  • id
  • code
  • name
  • category_id
  • category_code
  • category_name
  • benchmark_transactions
  • benchmark_ticket_size
  • base_operational_cost_rate

7. Public Content

GET /public/legal-documents?code=<code>

  • Auth: not required
  • Available codes:
    • terms_of_service
    • privacy_policy

GET /public/umkm-academy/home

GET /public/umkm-academy/topics

GET /public/umkm-academy/topic

GET /public/umkm-academy/article

Auth: not required

These endpoints are more suitable for:

  • content/academy integration
  • public information embedding

8. Internal Endpoints

The endpoints below exist today, but their status is internal-only:

  • POST /internal/notifications/whatsapp/otp
  • POST /internal/notifications/push/test
  • POST /internal/merchant/registration/review
  • POST /internal/merchant/registration/approve
  • POST /internal/merchant/status

For partner cooperation, these endpoints should:

  • not be opened directly to the public
  • not be used without a clear whitelist and security contract

Endpoints Most Ready for Partner Use Today

If the goal is reading merchant data already in production, the most ready endpoints today are:

  1. POST /auth/resolve-phone
  2. POST /auth/request-otp
  3. POST /auth/verify-otp
  4. GET /auth/profile
  5. GET /merchant/home-dashboard
  6. GET /merchant/transactions
  7. GET /references/business-types
  8. GET /references/business-scales
  9. region endpoints references/*

Important notes:

  • the endpoints above are not yet sufficiently safe for direct use by sales partners viewing all merchants
  • the endpoints above also do not yet separate finance/scoring-specific payloads
  • so for serious external partners, the existing endpoints are more suitable as a design basis, not a final contract

Architecture Decisions (Updated 2026-06-02)

  • db_kesles_merchant is the permanent core DB — no db_kesles_merchant_core will be created
  • merchant_core_api (port 8080) = "core-service" — no separate Go service will be built for the merchant domain
  • CORE_SERVICE_URL = http://localhost:8080 permanently — not a temporary placeholder
  • GET /internal/merchants/{id}/contact exists (returns name/phone/email/status); GET /internal/merchants/{id} also exists, returning code+name+referral_partner_id+status

Gaps That Still Exist Before Becoming a Formal Partner API

Status per 2026-06-02. ✅ = addressed, ❌ = still open.

  • /partner/... namespace — active as /api/partner/v1/
  • ✅ per-partner scoped API key — api_credentials with allowed_scopes array
  • ✅ per-partner audit log — every request logged to partner.api_audit_logs
  • ✅ formal API versioning /v1 — all partner endpoints under /api/partner/v1/
  • ✅ separation between referral/sales and finance/scoring partners — done via access_scope_mode + separate scoring.read scope
  • ✅ merchant filtering based on referral_code / partner-merchant mapping — ResolveMerchantScope enforces at query level
  • ✅ pagination and filter/sorting — active on referral-merchants and settlements endpoints
  • GET /internal/merchants/{id} with merchant_status — implemented (returns code+name+referral_partner_id+status), alongside the /contact subpath
  • ❌ explicit partner rate limiting — not yet implemented per-partner (only global server-side limits)
  • ❌ partner webhooks for transactions or merchant status — webhook config storage, delivery logging (partner.webhook_deliveries), and URL validation exist, but no event-driven dispatcher fires webhooks on transaction/merchant-status events yet
  • ❌ partner self-service portal — dashboard for partners to manage their own credentials and view their own analytics (planned post Phase 3)
  • ❌ explicit enforcement prohibiting terminal/device access in formal API contract — still no contract document; enforcement is code-only
  • ❌ per-partner-type data minimization policy (e.g. bank sees only scoring fields, not full tx detail) — app-level separation exists via scopes but no formal field-level masking policy document

Status per 2026-06-02. ✅ = done.

  1. ✅ determine which endpoints are allowed to be opened to partners
  2. ✅ create a Partner API Spec separate from the mobile/dashboard internal API
  3. ✅ add partner credentials (credential_key / credential_secret + HMAC option)
  4. ✅ create an audit log of all partner requests (partner.api_audit_logs)
  5. ✅ separate access policy for referral/sales partners and finance/scoring partners
  6. set up per-partner rate limiting (still open)
  7. prepare partner webhooks for transaction events and merchant status (planned Phase 3)
  8. publish formal API contract document with terminal/device access prohibition
  9. implement field-level data minimization policy per partner type

Source References

This document is based on the implementation currently active in:

  • merchant_core_api/internal/httpapi/routes_auth.go
  • merchant_core_api/internal/httpapi/routes_merchant.go
  • merchant_core_api/internal/httpapi/routes_reference.go
  • merchant_core_api/internal/httpapi/routes_public.go
  • merchant_core_api/internal/httpapi/routes_internal.go
  • merchant_core_api/internal/httpapi/server.go
  • merchant_core_api/internal/httpapi/merchant_home_dashboard_handlers.go
  • merchant_core_api/internal/httpapi/merchant_transaction_handlers.go
  • merchant_core_api/internal/httpapi/merchant_handlers.go
  • merchant_core_api/internal/httpapi/merchant_device_offer_handlers.go
  • merchant_core_api/internal/httpapi/reference_handlers.go
  • merchant_core_api/internal/httpapi/account_settings_handlers.go