Integration Steps
Integrate UPI One-Time Mandate with OT direct-execution subscriptions, mandate registration, and presentation execution.
Use this guide to integrate UPI One-Time Mandate (OTM) using the OT direct-execution subscription flow. In this flow, you create a no-plan OT subscription, register the bank mandate through the Payments API, and then execute the debit through the Presentation API after the subscription becomes active.
Ask your Pine Labs Online account manager to enable UPI OTM for your merchant account and confirm your public base URLs, approved transaction limits, validity window, supported MCC, and webhook configuration.
Scope
OT direct execution is a no-plan subscription product:
- No plan is created for the subscription.
execution_modeisDIRECT_EXECUTION.plan_details.frequencyisOT.- The subscription amount becomes the maximum amount available for the Presentation API.
- The existing Presentation API request and response schema is used.
- The bank mandate registration step is completed through the Payments API with
request_typeset toCREATE_MANDATE.
Integration flow
Create or reuse a Pine Labs Online customer profile and keep the customer_id available for subscription creation.
Create a no-plan OT direct-execution subscription. The response returns a subscription_id, an order_id, and status as CREATED.
Use the returned order_id to create a UPI payment with mandate_info.request_type set to CREATE_MANDATE.
Wait for the mandate callback or inquiry processing to move the subscription to ACTIVE.
Create a Presentation API request against the active subscription to execute the debit.
Authentication and headers
Subscription APIs
Include merchant context for subscription and presentation calls:
Merchant-ID: <merchant-id>
Authorization: Bearer <access-token>
Content-Type: application/json
Accept: application/json
Payments API
The Payments API call must use an access token generated by the Pine Labs authentication API.
Authorization: Bearer <access-token>
Content-Type: application/json
Accept: application/json
Request-ID: <uuid>
Request-Timestamp: <utc-timestamp>
Keep client_id, client_secret, access tokens, and merchant identifiers only on backend systems. Do not call Pine Labs authentication, subscription, presentation, or payment APIs directly from a browser or frontend client.
1. Generate token
Generate an access token from your backend server and use it in subsequent API calls.
curl --request POST \
--url <base-url>/api/auth/v1/token \
--header 'Content-Type: application/json' \
--header 'Request-ID: <unique-request-id>' \
--header 'Request-Timestamp: <iso-8601-timestamp>' \
--data '{
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>",
"grant_type": "client_credentials"
}'
Do not store API credentials, access tokens, or client secrets in frontend code, mobile apps, logs, screenshots, or public repositories.
2. Create a customer
Create or reuse a Pine Labs Online customer profile before initiating the mandate. Store the returned customer_id against your user profile.
curl --request POST \
--url <base-url>/api/v1/customer \
--header 'Authorization: Bearer <access-token>' \
--header 'Content-Type: application/json' \
--data '{
"merchant_customer_reference": "<merchant-customer-reference>",
"first_name": "Ananya",
"last_name": "Sharma",
"country_code": "91",
"mobile_number": "<customer-mobile-number>",
"email_id": "customer@example.com"
}'
3. Create OT subscription
Create an OT direct-execution subscription. The amount configured here is stored as the subscription maximum limit and controls how much can be debited later through the Presentation API.
<subscription-base-url>/api/v1/public/subscriptions/otCreate a no-plan OT direct-execution subscription.
curl --request POST \
--url <subscription-base-url>/api/v1/public/subscriptions/ot \
--header 'Authorization: Bearer <access-token>' \
--header 'Merchant-ID: <merchant-id>' \
--header 'Content-Type: application/json' \
--data '{
"merchant_subscription_reference": "ot-direct-<unique-reference>",
"customer_id": "<customer-id>",
"plan_details": {
"amount": 100,
"currency": "INR",
"validity_days": 30,
"description": "Security deposit and estimated charges"
},
"callback_url": "https://<your-domain>/subscription/callback",
"merchant_metadata": {
"source": "integration",
"case_id": "ot-direct"
}
}'
Amount rules
plan_details.amountis an integer value in the smallest currency unit. For INR, public subscription integration uses paisa, so100means INR 1.plan_details.amountis stored as the subscription maximum limit for Presentation API execution.- The OMS payment
payment_amount.valueused for mandate registration must matchplan_details.amount. - Presentation amounts must be less than or equal to the stored subscription maximum limit.
4. Register mandate through Payments API
Use the order_id returned from subscription creation to create a UPI mandate registration payment.
<payments-base-url>/api/pay/v1/orders/\{order_id\}/paymentsRegister the UPI OTM mandate through the Payments API.
curl --request POST \
--url <payments-base-url>/api/pay/v1/orders/<order-id>/payments \
--header 'Authorization: Bearer <access-token>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Request-ID: <uuid>' \
--header 'Request-Timestamp: <utc-timestamp>' \
--data '{
"payments": [
{
"payment_method": "UPI",
"merchant_payment_reference": "pay-<unique-reference>",
"payment_amount": {
"value": 100,
"currency": "INR"
},
"payment_option": {
"upi_details": {
"txn_mode": "INTENT"
}
},
"mandate_info": {
"request_type": "CREATE_MANDATE"
}
}
]
}'
Mandate registration rules
{order_id}must be theorder_idreturned by OT subscription creation.payment_amount.valuemust matchplan_details.amountfrom the subscription creation request.mandate_info.request_typemust beCREATE_MANDATE.- Use UPI intent mode for mandate authorization.
In simulator-based non-production testing, no separate bank app or challenge_url handling may be required if the simulator marks mandate authorization as successful. For production, follow the authorization behaviour configured for your environment.
5. Wait for subscription activation
After successful mandate authorization or inquiry processing, the subscription moves from CREATED to ACTIVE.
Do not create a presentation until the subscription is ACTIVE or RESUMED.
6. Fetch OT subscription by ID
Use the OT subscription fetch API to verify the subscription state before presentation.
<subscription-base-url>/api/v1/subscriptions/ot/\{subscription_id\}Fetch an OT direct-execution subscription by ID.
curl --request GET \
--url <subscription-base-url>/api/v1/subscriptions/ot/<subscription-id> \
--header 'Authorization: Bearer <access-token>' \
--header 'Merchant-ID: <merchant-id>'
Expected response:
subscription_idmatches the created subscription.statusisACTIVEbefore debit execution.execution_modeisDIRECT_EXECUTION.plan_details.frequencyisOT.
7. Filter subscriptions
You can filter OT subscriptions through the get-all subscriptions API.
<subscription-base-url>/api/v1/public/subscriptionsList subscriptions and filter OT direct-execution records.
curl --request GET \
--url '<subscription-base-url>/api/v1/public/subscriptions?frequency=OT&status=ACTIVE&page=1&size=20' \
--header 'Authorization: Bearer <access-token>' \
--header 'Merchant-ID: <merchant-id>'
Useful filters:
| Filter | Example |
|---|---|
| Frequency | frequency=OT |
| Status | status=CREATED or status=ACTIVE |
| Amount | amount=100&amount_range=isEqual |
| Pagination | page=1&size=20 |
| Sorting | sort=subscriptionId,desc |
8. Create presentation for active subscription
Create a presentation against the active OT-direct subscription to execute the debit.
<subscription-base-url>/api/v1/public/presentationsCreate a presentation for an active OT direct-execution subscription.
curl --request POST \
--url <subscription-base-url>/api/v1/public/presentations \
--header 'Authorization: Bearer <access-token>' \
--header 'Merchant-ID: <merchant-id>' \
--header 'Content-Type: application/json' \
--data '{
"subscription_id": "<subscription-id>",
"amount": {
"value": 100,
"currency": "INR"
},
"merchant_presentation_reference": "present-<unique-reference>",
"merchant_retry_id": "retry-<unique-reference>"
}'
Presentation rules
- The subscription must be
ACTIVEorRESUMED. amount.valuemust be less than or equal to the stored subscription maximum limit derived fromplan_details.amount.merchant_presentation_referencemust be unique for the merchant.due_dateis optional for OT direct execution. If supplied, it must not be in the past and must not be after the subscription end date.- Do not pass
order_idunless you intentionally want to execute against an existing OMS order. If omitted, the service creates a fresh OMS order for direct execution.
9. Fetch presentation
Fetch the presentation by presentation_id or by your merchant_presentation_reference.
<subscription-base-url>/api/v1/public/presentations/\{presentation_id\}Fetch a presentation by presentation ID.
curl --request GET \
--url <subscription-base-url>/api/v1/public/presentations/<presentation-id> \
--header 'Authorization: Bearer <access-token>' \
--header 'Merchant-ID: <merchant-id>'
<subscription-base-url>/api/v1/public/presentations/reference/\{merchant_presentation_reference\}Fetch a presentation by merchant presentation reference.
curl --request GET \
--url <subscription-base-url>/api/v1/public/presentations/reference/<merchant-presentation-reference> \
--header 'Authorization: Bearer <access-token>' \
--header 'Merchant-ID: <merchant-id>'
Expected response:
subscription_idmatches the OT-direct subscription.merchant_presentation_referencematches the request.order_idis present.statusreflects the current presentation execution state.
Webhooks
Configure webhook handling so your backend can update order state even if the customer closes the browser or app.
| Event | Triggered when | Merchant action |
|---|---|---|
| Subscription created | OT subscription is created | Continue to mandate registration |
| Subscription activated | Customer mandate authorization succeeds | Mark the order as payment secured |
| Subscription charged | Presentation debit is executed | Fulfil or close the order |
| Presentation pending | Presentation request is created | Track status and wait for final update |
| Presentation failed | Debit execution fails | Check failure reason and retry only if safe |
| Subscription completed or expired | Mandate lifecycle ends | Stop further debits against the subscription |
Error handling
| Scenario | Expected result | Recommended action |
|---|---|---|
| Duplicate subscription reference | Duplicate subscription validation error | Generate a unique merchant_subscription_reference per subscription |
| Missing or invalid amount | Validation error | Pass a positive amount in the smallest currency unit |
| Invalid validity | Validation error | Keep validity_days within your approved range |
| Presentation before activation | Invalid subscription state error | Wait until subscription status is ACTIVE or RESUMED |
| Presentation amount above ceiling | Requested amount exceeds maximum limit | Retry with an amount less than or equal to plan_details.amount |
| Duplicate presentation reference | Duplicate presentation validation error | Generate a unique merchant_presentation_reference per presentation |
| Wrong merchant fetch | Not found or unauthorized merchant-context response | Fetch using the same Merchant-ID used during creation |
Response statuses
| Status | Meaning | Merchant action |
|---|---|---|
CREATED | OT subscription has been created and is awaiting mandate approval | Continue with mandate registration and customer authorization |
ACTIVE | Mandate is approved and available for presentation execution | Use Presentation API to debit funds |
PENDING | Presentation request is created and processing is pending | Fetch presentation status or wait for webhook notification |
FAILED | Request failed | Check error code and retry only if applicable |
SUCCESS | Debit or operation completed successfully | Reconcile order and presentation state |
Go-live checklist
- OTM is enabled for your merchant account.
- Your approved amount limits and validity window are documented.
- Customer creation, OT subscription creation, mandate registration, subscription fetch, presentation create, and presentation fetch are tested end to end.
payment_amount.valuein the Payments API matchesplan_details.amountfrom subscription creation.- Presentation amount does not exceed the subscription maximum limit.
merchant_subscription_referenceandmerchant_presentation_referenceare unique per merchant.- Webhook signatures are verified before updating order state.
- Presentation is triggered only after fulfilment or final bill confirmation.
- Customer-facing screens explain that funds are blocked first and debited through presentation execution.
Operational verification checklist
| Check | Expected |
|---|---|
| Create OT subscription response | 201, generated subscription_id, generated order_id, status=CREATED, execution_mode=DIRECT_EXECUTION |
| Mandate registration payment | 2xx, data.status=PENDING, challenge_url may be present depending on environment |
| Authorization or inquiry processing | Subscription moves to ACTIVE |
| Fetch by ID | OT endpoint returns the created subscription |
| Get-all filters | frequency=OT, status, and amount filters include the subscription |
| Presentation create | Response includes presentation_id, status=PENDING, and execution order_id |
| Presentation fetch | Response includes matching subscription_id, merchant_presentation_reference, and order_id |
| Webhook processing | Downstream systems receive subscription and presentation events according to webhook configuration |
