Update Bank Details
Sparse update of bank fields (IBAN/SWIFT/holder/country/name) plus optional preferred-method choice. ANY bank field change resets the verified flag — admin must re-verify before BANK_TRANSFER becomes available again.
PATCH /api/v1/creators/bank-details — 🔑 Bearer · Rate limit: 10 req / hour
Sparse update of the creator's bank-transfer details. Validates IBAN format (^[A-Z]{2}[0-9]{2}[A-Z0-9]{4,30}$), SWIFT/BIC format (^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$), and ISO 3166-1 alpha-2 country code. Any change to a bank field (iban / bankName / accountHolderName / swiftCode / bankCountry) resets bankAccountVerified to false — the bank record must be re-verified by admin before BANK_TRANSFER becomes a usable payout method again.
Verification reset semantics. Submitting preferredPayoutMethod alone (no bank field changes) does not reset bankAccountVerified. Only changes to the five bank fields above flip verification off. Plan UX accordingly: a "switch preferred method" button is safe; a "edit bank details" button warns of pending re-verification.
The endpoint accepts updates even when verification is currently in flight or already passed. There is no idempotency / conflict gate — the rule is simply "any write resets verification". If you want to gate the UI on verification state, read it from GET /creators/payout-settings first.
Request
Body — UpdateBankDetailsDto
All fields optional. Send only what you want to change.
| Field | Type | Validation | Notes |
|---|---|---|---|
iban | string | MaxLength(34), regex ^[A-Z]{2}[0-9]{2}[A-Z0-9]{4,30}$ | Bank account |
bankName | string | MaxLength(100) | Display name |
accountHolderName | string | MaxLength(200) | Legal account holder |
swiftCode | string | MaxLength(11), regex ^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$ | SWIFT/BIC |
bankCountry | string | MaxLength(2), regex ^[A-Z]{2}$ | ISO 3166-1 alpha-2 |
preferredPayoutMethod | enum (PayoutMethod) | IsEnum(PayoutMethod) | One of: STRIPE_CONNECT / BANK_TRANSFER. Setting this does not reset verification. |
| Header | Required | Notes |
|---|---|---|
Authorization: Bearer <accessToken> | ✓ | JWT from POST /auth/login |
Response
200 OK — SuccessOnlyResponseDto
{
"success": true
}| Field | Type | Notes |
|---|---|---|
success | boolean | Always true on 200. Re-fetch via GET /creators/payout-settings to read the post-write state (including the reset verified: false flag if applicable). |
Errors
| HTTP | code / i18nKey | Reason |
|---|---|---|
400 | (DTO validation) | IBAN / SWIFT / country regex fail; field length over cap; invalid preferredPayoutMethod enum |
401 | (guard) | Missing / invalid bearer token |
404 | creator.payout.not_found | No CreatorProfile for this user |
429 | (throttle) | Rate limit exceeded (10 req/hour) |
Side effects
- Lookup
CreatorProfile.idbyuserId; thrownot_foundif missing. - Build sparse
dataobject from defined keys (the 5 bank fields +preferredPayoutMethod). - Verification reset gate — compute
bankFieldChanged = (iban|bankName|accountHolderName|swiftCode|bankCountry) supplied. Iftrue:data.bankAccountVerified = false.data.bankVerifiedAt = null.
prisma.creatorProfile.update({ where: { id }, data }).- Audit log:
[payout] Bank details updated for creator {id} (verified reset: <bool>).
Code samples
curl -X PATCH https://api.bio.re/api/v1/creators/bank-details \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"iban": "TR000000000000000000000000",
"bankName": "Example Bank",
"accountHolderName": "John Doe",
"swiftCode": "EXAMPTRIS",
"bankCountry": "TR"
}'type UpdateBankDetailsInput = {
iban?: string;
bankName?: string;
accountHolderName?: string;
swiftCode?: string;
bankCountry?: string;
preferredPayoutMethod?: 'STRIPE_CONNECT' | 'BANK_TRANSFER';
};
async function updateBankDetails(accessToken: string, input: UpdateBankDetailsInput): Promise<void> {
const res = await fetch('https://api.bio.re/api/v1/creators/bank-details', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(input),
});
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Update bank details failed'), {
code: json?.error?.code,
});
}
}import { useMutation, useQueryClient } from '@tanstack/react-query';
export function useUpdateBankDetails() {
const qc = useQueryClient();
return useMutation({
mutationFn: async (input: UpdateBankDetailsInput) => {
const res = await fetch('/api/v1/creators/bank-details', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(input),
});
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Update bank details failed'), {
code: json?.error?.code,
i18nKey: json?.error?.i18nKey,
});
}
},
onSuccess: () => {
// verified flag may have flipped to false — refresh the consolidated read
qc.invalidateQueries({ queryKey: ['creators', 'payout-settings'] });
qc.invalidateQueries({ queryKey: ['creators', 'profile'] });
},
});
}Try it
Authorization
bearer In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
curl -X PATCH "https://loading/api/v1/creators/bank-details" \ -H "Content-Type: application/json" \ -d '{}'{
"success": true
}{
"success": false,
"error": {
"code": "AUTH_UNAUTHORIZED",
"message": "Invalid credentials",
"i18nKey": "auth.login.invalid_credentials",
"i18nVars": {
"field": "email"
},
"details": [
{
"message": "email must be an email"
}
],
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}{
"success": false,
"error": {
"code": "AUTH_UNAUTHORIZED",
"message": "Invalid credentials",
"i18nKey": "auth.login.invalid_credentials",
"i18nVars": {
"field": "email"
},
"details": [
{
"message": "email must be an email"
}
],
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}{
"success": false,
"error": {
"code": "AUTH_UNAUTHORIZED",
"message": "Invalid credentials",
"i18nKey": "auth.login.invalid_credentials",
"i18nVars": {
"field": "email"
},
"details": [
{
"message": "email must be an email"
}
],
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Source
| Source | Path | Lines |
|---|---|---|
| Controller | apps/api-core/src/modules/creator/creator.controller.ts | 264–274 (updateBankDetails) |
| DTO (request) | apps/api-core/src/modules/creator/dto/creator.dto.ts | 136–160 (UpdateBankDetailsDto) |
| DTO (response) | apps/api-core/src/common/dto/common-response.dto.ts | SuccessOnlyResponseDto |
| Service | apps/api-core/src/modules/creator/creator.service.ts | 904–930 (updateBankDetails) |
| Prisma models | packages/prisma/prisma/schema.prisma | CreatorProfile bank fields, enum PayoutMethod |
Get Payout Settings
Consolidated read of bank details, Stripe Connect status, preferred method, and the available-methods bitmap. Owner-only.
Start KYC Verification
Initiate identity verification with the active KYC provider. Returns a redirect URL for the hosted verification flow. Vendor identity is admin-managed — clients receive an opaque provider id.