Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ency07/B2B/llms.txt

Use this file to discover all available pages before exploring further.

tenant_settings is a key-value store scoped by (tenant_id, module, config_key). It supports both plain and encrypted values. Public settings such as branding are readable anonymously via RLS; sensitive settings such as API keys are server-only and decrypted at read time via a Postgres RPC.

Server Actions

All three actions live in src/erp/actions/core.ts and are marked "use server".

getTenantSettings

getTenantSettings(tenantCode?: string | null): Promise<Record<string, any>>
Requires an active authenticated session and passes the caller through validateTenantAccess. For any row where is_encrypted = true, the action calls the get_tenant_setting Postgres RPC — with parameters p_tenant_id, p_module, and p_key — to decrypt the value server-side before returning it. The result is a flat Record<string, any> keyed by config_key.

getPublicTenantSettings

getPublicTenantSettings(tenantCode?: string | null): Promise<Record<string, any>>
No authentication required. Reads a fixed allow-list of public branding keys directly from tenant_settings. Safe to call from public-facing layouts and Next.js generateMetadata functions. Returns an empty object {} on query error rather than throwing. Public keys returned:
nombre_comercial    razon_social       logo_url
logo_secondary_url  favicon_url        primary_color
secondary_color     titulo_navegador   descripcion_sitio

updateTenantSettings

updateTenantSettings(
  tenantCode: string | null,
  module: string,
  key: string,
  value: any,
  isEncrypted?: boolean   // default: false
): Promise<Record<string, any>>
Requires the settings.manage permission (enforced via requireAction) and validateTenantAccess. Performs an upsert on the unique constraint (tenant_id, module, config_key) so calling it multiple times for the same key is always safe.

Usage Examples

Update a branding color

await updateTenantSettings(
  "acme",           // tenantCode
  "IDENTIDAD",      // module (matches saveTenantBranding module assignment)
  "primary_color",  // key
  "#2563EB",        // value
  false             // isEncrypted
);

Store an encrypted API key

await updateTenantSettings(
  "acme",
  "integrations",
  "sendgrid_api_key",
  "SG.xxxx",
  true  // isEncrypted — stored encrypted, decrypted via RPC on read
);

Read all tenant settings (authenticated)

const settings = await getTenantSettings("acme");
// settings["primary_color"] => "#2563EB"
// settings["sendgrid_api_key"] => "SG.xxxx" (already decrypted)

Read public branding (unauthenticated)

const branding = await getPublicTenantSettings("acme");
// branding["logo_url"] => "https://..."
// branding["primary_color"] => "#2563EB"

Database Schema

The tenant_settings table uses the following composite unique key:
ColumnTypeNotes
tenant_iduuidFK → tenants.id
moduletextLogical grouping, e.g. IDENTIDAD, EMPRESA, LOCALIZACION
config_keytextSetting identifier
config_valuejsonbPlain value or ciphertext
is_encryptedbooleanTriggers RPC-based decryption on read
is_publicbooleanControls anonymous read via RLS
deleted_attimestamptzSoft-delete; null = active row
The upsert conflict target is (tenant_id, module, config_key).
Encrypted values are decrypted server-side via the get_tenant_setting Postgres RPC. The encryption key is managed by Supabase Vault and never leaves the database layer. Never store secrets in plain config_value rows — always pass isEncrypted: true for API keys, webhook secrets, and other sensitive credentials.
The is_public = true flag on a tenant_settings row controls whether anonymous users can read it through the tenant_settings_read_anon RLS policy. Only mark branding display fields as public. Never set is_public = true on API keys, internal configuration, or encrypted rows.

Build docs developers (and LLMs) love