Create Deposit Intent
Create a deposit intent for a user with POST /external/deposits.


Create intent -> return depositUrl -> signed result webhook
POST /api/v1/external/depositsThis endpoint belongs to the Backend-to-Backend API category. When a user
starts a deposit, the partner site calls this endpoint server-side. Pientegra
reserves an eligible account, creates a transfer intent, and returns a signed
depositUrl to show to the user.
The final decision is not returned synchronously. After the user completes the payment, the deposit is approved or rejected and the result is delivered to the site backend by webhook.
For the user redirection model, see Hosted Deposit Page.
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | API key in Bearer <raw-api-key> format. |
Idempotency-Key | Yes | Unique UUID v4/v7 for this create operation. |
Content-Type | Yes | application/json |
Request body
{
"externalUserId": "user-12345",
"externalUserLabel": "Alex M.",
"amount": "100000",
"currency": "TRY"
}| Field | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | User ID on the partner site. |
externalUserLabel | string | No | Short human-readable label for this user in Pientegra, primarily for support conversations. Maximum 128 characters. |
amount | string or number | Yes | Deposit amount in minor units. 100000 = 1,000.00 TRY. Accepted range: 100 (1 TRY) to 100_000_000 (1,000,000 TRY). Values outside this range are rejected with amount_below_min or amount_above_max. |
currency | string | No | Defaults to TRY. The currently active currency is TRY. |
Response
{
"intentId": "01927b3c-9c1f-7a3a-9c4f-8a3e1f8b1c00",
"referenceNo": "D-2026-0000123",
"depositUrl": "https://pay.tenant.example/d/eyJhbGciOi...",
"requestedAmount": "100000",
"amount": "100000",
"currency": "TRY",
"expiresAt": "2026-04-28T12:30:00.000Z"
}| Field | Description |
|---|---|
intentId | Stable UUID for the deposit intent. Used in status lookup and webhook payloads. |
referenceNo | Short, human-readable reference searchable in support conversations and within Pientegra. |
depositUrl | Signed one-time URL to open for the user. |
requestedAmount | Original amount posted by the partner. Immutable. |
amount | Current approved amount. If adjusted to the actual received transfer amount, it may differ from requestedAmount; this is the amount to credit to the user balance. |
currency | ISO 4217 currency code. |
expiresAt | UTC timestamp when the deposit URL expires. The default token TTL is 30 minutes. |
Browser handoff
The hosted deposit page is shown to the user in the browser. If you use a popup,
open it during the user interaction and then set its location from the backend
response.
async function startDeposit(amount: string) {
const popup = window.open('about:blank', '_blank');
if (!popup) throw new Error('popup_blocked');
try {
const response = await fetch('/api/pientegra/deposit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount }),
});
const body = await response.json();
popup.location = body.depositUrl;
} catch (error) {
popup.close();
throw error;
}
}Your backend uses the Pientegra API key inside /api/pientegra/deposit. The API
key is never sent to the browser.
Status lifecycle
RESERVED
|-- PENDING_REVIEW
| |-- APPROVED
| `-- REJECTED
|-- AUTO_CANCELLED
`-- LATE_ARRIVAL
|-- APPROVED
`-- REJECTED| Status | Meaning |
|---|---|
RESERVED | Intent created, account reservation is active, and the user is expected to pay. |
PENDING_REVIEW | Funds arrived and are under review. |
APPROVED | Deposit approved. The site should credit the user balance from this event. |
REJECTED | Deposit rejected. |
AUTO_CANCELLED | The user did not pay before expiresAt. |
LATE_ARRIVAL | Funds arrived after expiry and require manual review. |
Webhook action
The primary signal for the site balance is the intent.approved webhook.
intent.created and intent.auto_cancelled are usually used only for logs or
support visibility.
The intent.approved webhook carries both requestedAmount and amount. Always
credit the user balance with amount, which is the approved amount actually
received. If amount !== requestedAmount, log the discrepancy on the partner side
and make it visible to support.
Error cases
| HTTP | error | Typical cause |
|---|---|---|
| 400 | validation_failed | Required field is missing, amount format is invalid, or partner provisioning is incomplete. |
| 401 | unauthorized | API key is invalid, inactive, or expired. |
| 409 | idempotency_key_reuse_with_different_body | The same key was reused with a different body. |
| 409 | duplicate_idempotency_key | A request with the same key is still in-flight. |
| 409 | collateral_exceeded | Partner collateral is not sufficient for the deposit reservation. |
| 503 | all_accounts_capped | No eligible active account capacity remains. |
| 429 | rate_limited | Rate limit exceeded. |
For the full list, see Error Reference.