Overview
Organization governance is four independent mechanisms:Reading and updating the policy
Reading the policy is open to any org member with a
PolicyManagement key. Writing it is Master-only.PATCH:
- Turning on
businessHoursEnabledrequiresbusinessHoursStart,businessHoursEndandbusinessHoursTimezonein the same request. Missing any of them is a400. dailySpendingLimitUsdmust be a positive numeric string. Pass explicitnullto clear the limit.- Every change writes an
Org Policy Updatedaudit row.
What each policy does
forceEmployeeKyc
When the policy requires KYC and the caller is not verified, every spend-capable request is rejected with 403. KYC applies to the user, and is never bypassed because the transport is an API key.
businessHoursEnabled
The current time is evaluated in businessHoursTimezone, for Employees only. A request outside the businessHoursStart–businessHoursEnd window, or on a weekday not listed in businessHoursDays, is rejected with 403.
Pure key-management steps are exempt, so an employee can still accept a wallet handover or rotate their keys after hours. Anything that signs, exports or spends never qualifies for that exemption.
forceAddressBook
Every destination is checked against the Corporate Address Book (see Organization Wallets). Only verified entries count. An unlisted destination is rejected with 400 "ADDRESS_NOT_WHITELISTED: <address>".
dailySpendingLimitUsd
See the next section.
Daily spending limits
resetAt is always the next UTC midnight regardless of the org’s business-hours timezone. Available to Master and Employee; requires ViewBalances.
How a spend is charged
The limit is evaluated in USD, per organization, and it is charged when the spend is created. Over the limit, the request is refused with403 "Action blocked: Daily spending limit exceeded." If a token’s price cannot be resolved while a limit is active, the spend is blocked rather than let through.
The amount charged is the one the server reads out of the transaction itself, not one the client reports, so an employee cannot understate a spend to stay under the limit. The SDK handles that exchange inside sendTransaction; there is nothing for you to call.
Limit groups
A limit group caps how many wallets and vaults the organization may hold per chain. It is provisioned platform-side; the SDK only reads it.limits. Master or Employee; requires ViewBalances. A 404 here means something is wrong. Treat it as a hard failure, not as “no group”.
Policy rejections in the SDK
OrgPolicyRejectedError is the intended typed surface for these, carrying platformCode: OrgPolicyCode.
Detecting a policy rejection as things stand:
The HTTP status lives in
error.statusCode on ApiError but in error.details.statusCode on
PermissionDeniedError, which is why the helper reads it from two places. Both carry the
server’s message on error.message. See Errors.instanceof OrgPolicyRejectedError check keyed on error.platformCode, touching only your catch
block.
Audit logs
AuditLogAccess.
An unrecognised value in eventTypes is a 400.
Event types
OrgAuditEventType enumerates the labels the SDK types. Each is triggered by:
More labels appear in the response than the SDK’s union covers. Treat
item.event as a string
when matching, and do not assume the union is exhaustive.CSV export
AuditLogAccess. The method resolves with the raw fetch Response so you can stream it.
getOrgAuditLogs, minus pagination. The export streams the entire filtered result set.
The response is Content-Type: text/csv with Content-Disposition: attachment; filename="org-audit-logs-YYYY-MM-DD.csv". The header row is fixed:
Date is the ISO 8601 timestamp, Metadata is the JSON blob serialized and CSV-escaped (embedded quotes doubled). Fields containing a comma, quote, or newline are quoted.