Endpoint
Update the public profile
/v1/merchantOnly support_email, branding.logo, branding.accent_color (Verified tier) and default_order_expiry_seconds. The profile is one for both modes and is what live payers see, so it is changed with a live key (or in the dashboard): a test key reads it but cannot change it (Test mode: a test key sees and creates only test objects), and answers livemode_mismatch with reason live_profile once its input is checked. Nothing is changed.
- Scope a restricted key needs
merchant:write- Publishable key
- Not allowed: secret or restricted key only
- Idempotency-Key
- Not used. A PATCH is already safe to repeat.
Parameters
| Parameter | Type and place | Meaning |
|---|---|---|
Orrerie-Version | stringheader | API version date. Default: the version the key was created with. Example 2026-10-11. |
Request body
A JSON object. The body may be left out. A field the call does not list is refused with parameter_unknown.
| Field | Type | Meaning |
|---|---|---|
support_email | string | The address payers are shown for questions about an order. |
branding | object | {logo: file_ id, accent_. The accent colour must reach a contrast of 4.5:1 against the checkout's dark field (#0A0D0B), and a text colour must read on it at 4.5:1; otherwise it is refused with accent_too_dark. |
default_order_expiry_seconds | integer | 900 to 604,800. |
Example request
curl
curl -X PATCH https://api.orrerie.com/v1/merchant \
-H "Authorization: Bearer $ORRERIE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"support_email": "support@acme.example"
}'JavaScript
const response = await fetch("https://api.orrerie.com/v1/merchant", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.ORRERIE_SECRET_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"support_email": "support@acme.example"
}),
});
const result = await response.json();
if (!response.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
console.log(result);Python
import os
import requests
response = requests.patch(
"https://api.orrerie.com/v1/merchant",
headers={"Authorization": f"Bearer {os.environ['ORRERIE_SECRET_KEY']}"},
json={
"support_email": "support@acme.example",
},
timeout=30,
)
result = response.json()
if not response.ok:
raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
print(result)PHP
<?php
$request = curl_init('https://api.orrerie.com/v1/merchant');
curl_setopt_array($request, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ORRERIE_SECRET_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'support_email' => 'support@acme.example',
]),
]);
$body = curl_exec($request);
if ($body === false) {
throw new RuntimeException(curl_error($request));
}
$result = json_decode($body, true);
if (curl_getinfo($request, CURLINFO_HTTP_CODE) >= 400) {
throw new RuntimeException($result['error']['code'] . ': ' . $result['error']['message']);
}
print_r($result);Responses
200 OK
The answer is the merchant object. Headers: Orrerie-Request-Id.
Example response, 200
{
"id": "mer_93r9sfyrrm5km287343znd4f",
"object": "merchant",
"livemode": true,
"created": "2026-09-02T09:15:44.020Z",
"name": "Acme Coffee Roasters",
"slug": "acme",
"profile_url": "https://pay.orrerie.com/m/acme",
"country": "US",
"business_type": "company",
"support_email": "support@acme.example",
"tier": "unverified",
"restricted": false,
"settlement": {
"mode": "convert",
"asset": "base:usdc"
},
"fee_bearer": "payer",
"default_order_expiry_seconds": 3600,
"rails": {
"orrerie_app": true,
"relay": true,
"direct": true
},
"app_pay_allowed": true,
"branding": {
"logo": "file_xn5cz8fk5zmyx3kp37rvddgs",
"accent_color": null
},
"limits": {
"order_max": {
"amount": "1000.00",
"currency": "USD"
},
"rolling_30_days_max": {
"amount": "5000.00",
"currency": "USD"
},
"lifetime_max": {
"amount": "20000.00",
"currency": "USD"
},
"open_orders_max": 50,
"outside_payouts_daily_max": {
"amount": "1000.00",
"currency": "USD"
},
"payout_batch_rows_max": 0,
"members_max": 2,
"live_keys_max": 2,
"webhook_endpoints_max": 1,
"requests_per_minute": 60
},
"usage": {
"rolling_30_days_paid": {
"amount": "1284.50",
"currency": "USD"
},
"lifetime_paid": {
"amount": "1284.50",
"currency": "USD"
},
"open_orders": 3,
"outside_payouts_today": {
"amount": "0.00",
"currency": "USD"
},
"members": 2,
"live_keys": 1,
"webhook_endpoints": 1
}
}Any other status: an error
A status of 400 or above answers with the error object. Branch on its code, never on its message.
Errors
Beside the errors every call can answer with (a bad key, a missing scope, the rate limit), this call answers with:
| Code | HTTP | Meaning |
|---|---|---|
passkey_approval_required | 403 | The action can only be done by a signed-in member in the dashboard (settlement asset, key-holding roles, approvals). No API key can do it. |
verification_required | 403 | The feature or value needs the Verified tier. |
livemode_mismatch | 400 | An id of the other mode was used: a test key naming a live object or the reverse. |
accent_too_dark | 400 | The accent colour does not reach a contrast of 4.5:1 against the checkout's dark field, or no text colour reads on it at 4.5:1; see reason. Choose a lighter colour. reason is field (the dark field) or ink (the text on it); the dashboard checks the same rule while you type. |