Users and profiles

All routes require x-api-key; none requires an API-key permission flag. Profile routes also require Authorization: Bearer <session> and operate on the session’s current profile/team. The user routes accept either a valid session or a team/profile-scoped API key; header-derived context is intentionally disabled for those routes.

API-key failures return 403, rate limits return 429, and required-session failures return 401. Error bodies use the standard error envelope.

Endpoints#

MethodPathContextSuccess
GET/v1/profileSession200
GET/v1/profile/banksSession200
POST/v1/profile/banksSession201
GET/v1/profile/banks/{id}Session200
PATCH/v1/profile/banks/{id}Session200
DELETE/v1/profile/banks/{id}Session200
POST/v1/profile/image/upload-urlSession200
POST/v1/profile/imageSession200
PATCH/v1/profile/metadataSession200
PATCH/v1/profile/usernameSession200
GET/v1/user/profilesSession or scoped key200
PUT/v1/user/default-profileSession or scoped key200
PATCH/v1/user/default-profileSession or scoped key200

GET /v1/profile#

Returns the current profile with its team membership, team, banks, user summary, verification summary, wallets, and optional image URL.

Headers: x-api-key and Authorization: Bearer <session> (required).

{
	"success": true,
	"data": {
		"profile": {
			"id": "profile-uuid",
			"nickname": "johndoe",
			"metadata": {},
			"teamMembers": { "role": "admin" },
			"team": { "id": "team-uuid", "name": "John's Team" }
		},
		"banks": [
			{
				"bank": { "id": "bank-uuid", "accountAlias": "Main" },
				"currency": { "symbol": "USD" }
			}
		],
		"user": {
			"country": "US",
			"firstName": "John",
			"lastName": "Doe",
			"language": "en",
			"username": "johndoe",
			"referralCode": "ABCD1234",
			"referralsCount": 0
		},
		"kyc": { "isVerified": false, "status": "none" },
		"wallets": [],
		"imageUrl": "/profile/profile-uuid/image-hash"
	}
}

imageUrl is omitted when no image is configured. A missing profile context returns 400 with APP_V1_PROFILE_ROUTE_01 (Missing profile context). Profile lookup failures use the propagated profile-read error and 404.

Profile banks#

Bank routes use the current session profile. {id} is the bank account ID. A bank can only be fetched, changed, or deleted through the current profile; these routes do not provide a separate team-admin override.

GET /v1/profile/banks#

Headers: x-api-key and Authorization: Bearer <session> (required).

{
	"success": true,
	"data": [
		{
			"bank": {
				"id": "bank-uuid",
				"accountName": "John Doe",
				"accountAlias": "Main",
				"accountType": "CHECKING",
				"accountNumber": "1234567890",
				"country": "US",
				"bankSlug": "chase",
				"additionalData": {}
			},
			"currency": { "symbol": "USD" }
		}
	]
}

A missing profile context returns 400 with APP_V1_PROFILE_BANKS_ROUTE_02. Read failures return 500 with the propagated error.

POST /v1/profile/banks#

Headers: x-api-key, Authorization: Bearer <session>, and Content-Type: application/json.

FieldTypeRequiredConstraints
accountNamestringYesNon-empty.
accountAliasstringYesNon-empty.
accountTypeenumYesCHECKING, SAVINGS, BUSINESS, CURRENT, MONEY_MARKET.
accountNumberstringYesNon-empty.
countrystringYesExactly two characters.
currencySymbolstringYesNon-empty and must resolve to a supported currency.
bankSlugstringYesNon-empty.
additionalDataobjectNoDefaults to {} and is validated against the selected country’s bank-field schema.

Returns 201 with the created bank account in data.

{
	"success": true,
	"data": {
		"id": "bank-uuid",
		"accountName": "John Doe",
		"accountAlias": "Main",
		"accountType": "CHECKING",
		"accountNumber": "1234567890",
		"country": "US",
		"bankSlug": "chase",
		"additionalData": {}
	}
}

Malformed bodies, missing context, and unsupported currency symbols return 400; missing context and invalid currency symbol currently share route code APP_V1_PROFILE_BANKS_ROUTE_01. Creation failures return 500 with the propagated error.

GET /v1/profile/banks/{id}#

Returns 200 with the matching bank account in data.

StatusCodeMessage
400APP_V1_PROFILE_BANKS_ID_ROUTE_01Missing profile or team / Missing bank ID
404APP_V1_PROFILE_BANKS_ID_ROUTE_01Bank account not found
500propagatedBank lookup failed

PATCH /v1/profile/banks/{id}#

FieldTypeRequiredConstraints
accountNamestringNoNon-empty if provided.
accountAliasstringNoNon-empty if provided.

Returns 200 with the updated bank account. Missing context, path ID, or an invalid body returns 400. An update failure is returned as 404 with its propagated error.

DELETE /v1/profile/banks/{id}#

There is no request body.

{
	"success": true,
	"data": { "deleted": true }
}

Missing context or path ID returns 400 with APP_V1_PROFILE_BANKS_ID_ROUTE_01. Deletion failures are returned as 403 with the propagated error.

Profile image#

Both routes require x-api-key, Authorization: Bearer <session>, and JSON bodies. Allowed filename extensions are jpg, jpeg, png, webp, and gif; allowed MIME types are image/jpeg, image/jpg, image/png, image/webp, and image/gif.

POST /v1/profile/image/upload-url#

FieldTypeRequired
fileNamestringYes
contentTypeallowed MIME enumYes
{
	"success": true,
	"data": {
		"uploadUrl": "https://storage.example/signed-upload",
		"filePath": "profile-images/profile-uuid/1760000000000-hash.jpg",
		"expiresAt": "2026-07-28T18:00:00.000Z"
	}
}

Missing context or an invalid body returns 400; route code for missing context is V1_PROFILE_IMAGE_UPLOAD_URL_01. Upload URL generation failures return 500 with V1_PROFILE_IMAGE_UPLOAD_URL_02.

POST /v1/profile/image#

Call after uploading the file to the signed URL.

FieldTypeRequired
filePathnon-empty stringYes
{
	"success": true,
	"data": { "imageUrl": "/profile/profile-uuid/image-hash" }
}

Missing context or an invalid body returns 400; route code for missing context is V1_PROFILE_IMAGE_CONFIRM_01. Update failures return 500 with V1_PROFILE_IMAGE_CONFIRM_02.

PATCH /v1/profile/metadata#

Merges fields into current profile metadata.

Headers: x-api-key, Authorization: Bearer <session>, and Content-Type: application/json.

FieldTypeRequiredConstraints
namestringNo
imagestringNoValid URL.
any other keyany JSON valueNoCustom metadata is accepted.

Returns 200 with the updated profile object plus optional imageUrl.

{
	"success": true,
	"data": {
		"id": "profile-uuid",
		"nickname": "johndoe",
		"metadata": { "name": "John Doe", "customField": "value" },
		"imageUrl": "/profile/profile-uuid/image-hash"
	}
}

imageUrl is omitted unless stored profile-image metadata is present. Missing context returns 400 with APP_V1_PROFILE_METADATA_ROUTE_01; malformed bodies return 400; update failures return 500.

PATCH /v1/profile/username#

Headers: x-api-key, Authorization: Bearer <session>, and Content-Type: application/json.

FieldTypeRequiredConstraints
nicknamestringYes4–50 characters; letters, digits, _, and -; cannot start with api_.

Returns 200 with the updated profile. Missing context returns 400 with APP_V1_PROFILE_USERNAME_ROUTE_01; malformed bodies return 400; any nickname update failure, including a conflict, is returned as 409 with the propagated error.

GET /v1/user/profiles#

Returns all profiles belonging to the context profile’s user.

Headers

HeaderRequired
x-api-keyYes
AuthorizationRequired unless the API key is already team/profile-scoped

x-profile-id and x-team-id do not establish context on this route.

{
	"success": true,
	"data": {
		"profiles": [
			{
				"id": "profile-uuid",
				"teamId": "team-uuid",
				"teamName": "John's Team",
				"appId": "app-uuid",
				"appName": "Example App",
				"nickname": "johndoe",
				"isDefault": true
			}
		]
	}
}

Missing context returns 400 with APP_V1_USER_PROFILES_ROUTE_01 (Profile and team context required). Invalid ownership/context lookup returns 403; list failures return 500.

PUT or PATCH /v1/user/default-profile#

PATCH is an alias of PUT; both have the same contract. The target profile must belong to the same user as the active context profile.

Headers

HeaderRequired
x-api-keyYes
AuthorizationRequired unless the API key is already team/profile-scoped

x-profile-id and x-team-id do not establish context on this route.

FieldTypeRequiredConstraints
profileIdstringYesUUID.
{
	"success": true,
	"data": { "defaultProfileId": "profile-uuid" }
}
StatusCodeMessage
400APP_V1_USER_DEFAULT_PROFILE_ROUTE_01Profile and team context required
400body parser codeInvalid body
404APP_V1_USER_DEFAULT_PROFILE_ROUTE_02Profile not found
403APP_V1_USER_DEFAULT_PROFILE_ROUTE_03Profile does not belong to you
403propagatedActive context is invalid
500propagatedTarget lookup or update failed