Push Provisioning
Adding a card to a wallet from inside your own app, and what GenerateTimeBasedSecret is actually for.
Push provisioning, also called in-app provisioning, is the "Add to Google Pay" button inside your own app. The cardholder taps once and the card lands in the wallet. No card number typed, no OTP.
It is the better experience by a wide margin, and wallet programmes increasingly require it rather than merely allowing it.
Why there is no OTP
Manual provisioning needs a one-time code because the wallet has no idea who typed the card number in. Push provisioning does not have that problem: the cardholder is inside your app, already authenticated by you. That authentication is what replaces the OTP.
This puts a requirement on you. The button must sit behind real authentication. If a user can reach "Add to Google Pay" without having properly logged in, you have removed the only control protecting the flow.
The path
%%{init: {"sequence": {"actorMargin": 8, "boxMargin": 6, "noteMargin": 6, "messageMargin": 26, "width": 110, "diagramMarginX": 8, "diagramMarginY": 8}}}%%
sequenceDiagram
autonumber
participant CH as Cardholder
participant APP as Your app
participant BE as Your backend
participant UDT as UnDosTres
participant W as Wallet
participant MC as Mastercard MDES
CH->>APP: Authenticates
CH->>APP: Taps Add to Google Pay
APP->>BE: Requests provisioning data
BE->>UDT: GenerateTimeBasedSecret
UDT-->>BE: secret, valid 60s
BE-->>APP: card_id + secret
APP->>W: Push provisioning via SDK
W->>MC: Checks eligibility
MC-->>W: Terms and conditions
W-->>CH: Displays the terms
CH->>W: Accepts
W->>MC: Starts digitization
MC->>UDT: Token auth request
UDT-->>MC: Approved
MC->>MC: Activates the token
MC->>UDT: Digitization complete
UDT->>BE: digitization.complete
MC-->>W: Provisioning complete
W-->>CH: Card appears in the wallet
What you build
| Piece | Who builds it |
|---|---|
| The "Add to wallet" button and its auth gate | You |
| Calling your backend for provisioning data | You |
Calling GenerateTimeBasedSecret | You (backend) |
Handing card_id + secret to the SDK | You (app) |
| The provisioning SDK itself | Supplied by UnDosTres |
| TAV calculation | UnDosTres |
| Card data encryption to the wallet | UnDosTres and the SDK |
| The provisioning decision | UnDosTres |
digitization.complete handling | You (backend) |
Two things are worth saying plainly, because they are the parts partners most often assume they have to build:
- You do not calculate the TAV. The Token Authentication Value is an encrypted signature derived from the card number, expiry and CVV, which authenticates the push request. UnDosTres calculates it and passes it to MDES. You never see the card data it is derived from.
- You do not implement the card-data encryption. The encrypted package that reaches MDES through the wallet provider is produced by UnDosTres and the SDK. Your app passes an opaque payload through.
GenerateTimeBasedSecret
GenerateTimeBasedSecretEndpoint: POST /debit/v1/digitization/generate-time-based-secret
This is the one tokenization endpoint whose purpose is not obvious from its name, so it is worth being explicit.
Push provisioning needs to prove to UnDosTres that the request genuinely came from your authenticated app, without your app ever handling the card number. The mechanism is a short-lived shared secret bound to a specific card.
You call it with a card_id and get back a secret:
{
"text": "Success",
"timestamp": "2026-08-19T10:25:21.857267676-06:00",
"endpoint": "/debit/v1/digitization/generate-time-based-secret",
"error": {},
"payload": {
"secret": "123#45236521"
}
}The secret has two parts separated by #:
| Part | Meaning |
|---|---|
123 | Key ID. Identifies which shared key was used, so keys can be rotated without downtime |
45236521 | A time-based one-time password, derived from the shared key and the card_id |
The secret is valid for 60 seconds. Generate it at the moment the cardholder taps the button, not in advance, and never cache it.
Rules
- Generate it server side. The shared key must never reach the app.
- One secret per provisioning attempt. If the cardholder abandons and retries, generate a new one.
- Pass
card_idandsecrettogether. Neither is useful alone. - Clock accuracy matters. The secret is time-based, so a backend whose clock has drifted will produce secrets that fail validation. Keep NTP running. An incorrect clock is the most common cause of unexplained failures on this call.
Parameters still to be confirmed. The shared key for your programme, its key ID, and the exact secret length, hashing algorithm and validity window are being confirmed and will be issued to you directly, not published here. Until you have them you can integrate against the endpoint's shape, but you cannot complete an end-to-end push provisioning test. We will send the key material through a secure channel, never by email.
Availability
Push provisioning depends on the provisioning SDK and on the shared key material above. Both are pending. The backend endpoint is documented and stable; the mobile side is not yet ready to build against.
If you are sequencing work: build the backend call and your digitization.complete handling now, and hold the app-side integration until the SDK and key are issued.
Related
- Manual Provisioning — the path that does need an OTP
- Provisioning Overview — push versus manual
- Token Lifecycle Management
Updated about 2 hours ago

