# OSchema for freezone/main # ═══════════════════════════════════════════════════════════════════════════════ # OSIS Schema Definition # ═══════════════════════════════════════════════════════════════════════════════ # # RPC Endpoint: POST /{context}/{domain}/rpc # Content-Type: application/json # # Request format (JSON-RPC 2.0): # {"jsonrpc": "2.0", "method": "TypeName.method", "params": {...}, "id": 1} # # Available CRUD methods for root objects: # - TypeName.new -> Create new object (returns empty template) # - TypeName.get -> Get by sid: {"sid": "..."} # - TypeName.set -> Create/update: {"data": ""} # - TypeName.delete -> Delete by sid: {"sid": "..."} # - TypeName.list -> List all sids # - TypeName.find -> Search: {"query": "..."} # # ═══════════════════════════════════════════════════════════════════════════════ # ===SCHEMA=== # ─────────────────────────────────────────────────────────────────────────────── # ROOT OBJECTS - Storable entities with CRUD operations # ─────────────────────────────────────────────────────────────────────────────── # Admin audit log entry [rootobject] AdminAction = { sid: str # SmartID for storage admin_email: str @index # Admin who performed the action action: str @index # Action type (e.g., "suspend_user", "approve_kyc") target_type: str # Entity type affected (user, resident, company, payment) target_id: str @index # ID of affected entity details: str # JSON details of the action created_at: otime # When the action was performed } # API key for reseller access [rootobject] ApiKey = { sid: str # SmartID for storage key_hash: str @index # Hashed API key (SHA-256 hex) name: str @index # Key name/description user_id?: str # Optional user reference is_active: bool # Key active status last_used_at?: otime # Last usage timestamp created_at: otime expires_at?: otime # Optional expiration discount_profile?: str # Discount profile name (e.g. "tftc"), maps to [discounts.] in branding.toml allow_company_creation: bool # Whether this key can create companies (default false) mcp_scope?: str # MCP access scope: "read" or "readwrite" (default: none = no MCP access) max_transaction_amount?: f64 # Max amount per MCP transaction (default 100.0, 0 = unlimited) # Tier 2: Aggregate spending limits daily_spending_limit?: f64 # Max EUR per calendar day (default 500.0, 0 = unlimited) monthly_spending_limit?: f64 # Max EUR per calendar month (default 2000.0, 0 = unlimited) # Tier 3: Rate limiting max_calls_per_hour?: i32 # Max MCP calls per hour (default 60, 0 = unlimited) max_calls_per_day?: i32 # Max MCP calls per calendar day (default 500, 0 = unlimited) # Tier 4: Granular tool permissions allowed_tool_patterns?: str # Comma-separated glob patterns for allowed tools (empty = all allowed) blocked_tool_patterns?: str # Comma-separated glob patterns for blocked tools (checked first) # Tier 6: Approval queue approval_threshold?: f64 # Amount above which approval is required (0 = off) approval_required_tools?: str # Comma-separated tool patterns that always need approval # Partner track (D-018, s60): identifies which partner owns this reseller key. # Non-unique — a partner may rotate keys during a cutover window. Lookup via [partner_config.] in branding.toml. partner_id?: str @index # Partner identifier (e.g. "tftc") — None for personal keys and pre-s60 reseller keys # Auto-minted by the platform on resident creation to power the AI chat proxy. # Hidden from `list_personal` so users don't see a system row in their Profile API Keys # panel and accidentally revoke the key the AI proxy depends on. is_system_minted: bool # True for keys auto-created by the platform; user-minted keys are false } # ─────────────────────────────────────────────────────────────────────────────── # BUSINESS DOCUMENTS # ─────────────────────────────────────────────────────────────────────────────── # Uploaded or generated business document [rootobject] BusinessDocument = { sid: str # SmartID for storage (doc_id) company_id: str @index # Reference to FreeZoneCompany document_type: BusinessDocumentType # Certificate, License, Memorandum, etc. name: str @index # Document name description?: str # Document description file_url?: str # URL to file content?: str # Content for generated docs is_official: bool # Whether issued by FreeZone issued_date?: otime # When document was issued expiry_date?: otime # When document expires uploaded_by?: str # Resident ID who uploaded created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Company message entity [rootobject] CompanyMessage = { sid: str # SmartID for storage company_id: str @index # Reference to FreeZoneCompany title: str @index # Message title body: str @index # Message body/content message_type: MessageType # Type (system/notification/alert) category?: MessageCategory # Category action_url?: str # URL for action button action_label?: str # Label for action button is_read: bool # Read status created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # CompanyRole — role + time + classification, the primitive from FREEZONE.md principle 2. # One row per (subject × role × company × validity interval). Append-only (principle 3): # supersede by closing valid_to and writing a new row, never edit in place. # # subject_id is a formatted ID string, prefix-discriminated: # "ZDFZ-YYYY-RA..." → DigitalResident # "ZDFZ-C-YYYY-..." → CorporateShareholder (M2 forward-compat per e2e §X M1-ROLE-12) # company_id is the formatted company/coop ID (ZDFZ-YYYY-Y... for FZC, ...-P... for coop). # classification_scope enumerates the audience tiers for which this role is visible when # DocumentAudience or visibility filters compute (current_roles × classification) in a later session. # [rootobject] CompanyRole = { sid: str # SmartID for storage company_id: str @index # Formatted company ID (ZDFZ-YYYY-Y/P...) subject_id: str @index # Formatted subject ID; prefix discriminates variant role: CompanyRoleKind @index # shareholder / founding_member / platinum_member / founder valid_from: otime @index # Inclusive — role is held from this instant valid_to?: otime # Exclusive — role ends at this instant (None = still current) classification_scope: [str] # Audience tiers (e.g. ["public","shareholder","founder"]) created_at: otime created_by: str # Formatted ID of the actor who wrote this row } # Business Contract entity [rootobject] Contract = { sid: str # SmartID for storage fzc_id: str @index # Reference to FreeZoneCompany name: str @index # Contract name contract_type: str @index # Contract type counterparty: str @index # Other party name status: ContractStatus # Contract status start_date: otime # Start date end_date?: otime # End date total_value?: f64 # Contract value created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Contract Signature entity [rootobject] ContractSignature = { sid: str # SmartID for storage (signature_id) user_id: str @index # Reference to User contract_id: str @index # Reference to RegistrationContract contract_version: str # Version that was signed receipt_id: str # Unique signing receipt identifier (non-guessable) signed_at: otime # Signature timestamp entity_type: str # "user", "digital_resident", "company" entity_name: str # Full name of signing entity entity_id: str # Empty for user, DR SID, Company SID ip_address?: str # IP address at signing created_at: otime # Creation timestamp } # Contract Template entity [rootobject] ContractTemplate = { sid: str # SmartID for storage name: str @index # Template name description: str @index # Template description category: str @index # Template category content: str # Template content created_at: otime # Creation timestamp } # Credit balance per user [rootobject] CreditBalance = { sid: str # SmartID for storage user_id: str @index # Reference to User (one balance per user) balance: f64 # Current credit balance (USD) lifetime_earned: f64 # Total credits ever earned/purchased lifetime_spent: f64 # Total credits ever spent last_top_up_at?: otime # When last top-up occurred created_at: otime # Creation timestamp updated_at: otime # Last update timestamp # Auto top-up settings auto_topup_enabled: bool # Whether auto top-up is active auto_topup_amount: f64 # Amount to top up auto_topup_threshold: f64 # Trigger when balance drops below this stripe_customer_id?: str # Stripe customer ID for auto top-up stripe_payment_method_id?: str # Stripe payment method for auto top-up } # Credit transaction ledger entry [rootobject] CreditTransaction = { sid: str # SmartID for storage user_id: str @index # Reference to User tx_type: CreditTransactionType # top_up, deduction, refund, bonus, expiry amount: f64 # Transaction amount (always positive) balance_after: f64 # Balance after this transaction currency: str # Currency code (USD) description: str @index # Human-readable description reference_type?: str # What triggered this: "payment", "kyc", "subscription", "company", "admin", "ai_call" reference_id?: str @index # ID of the triggering entity (payment_id, resident_id, etc.) external_call_id?: str @index # S59/D-017 — stable external idempotency key (e.g. aibroker call_id). Single-field @index + leader-elected poll guards double-debit; OSIS has no @unique primitive. status: CreditTransactionStatus # pending, completed, failed created_at: otime # Creation timestamp } # Digital Resident entity [rootobject] DigitalResident = { sid: str # SmartID for storage (internal ID) resident_id: str @index # Formatted ID like ZDFZ-2026-RA000001 # Owner user reference. Optional as of π-2a (D-019, s61): partner-onboarded DRs have # user_id=None — they are typed actors under Partner, not freezone users (principle 4). # All pre-s61 rows have Some(_) — migration is additive, no rewrite of existing data. user_id?: str @index # Partner that onboarded this DR (π-2a, D-019, s61). None for freezone-native DRs. # Ownership comparisons MUST use (user_id, partner_id) tuple — never bare user_id — # because None==None would otherwise create cross-partner authorisation bypass. partner_id?: str @index email: str @index # Email address first_name: str @index # First name last_name: str @index # Last name kyc_provider_id: str # ID from KYC provider (iDenfy) phone: str # Phone number status: ResidentStatus # Current status public_key?: str # Ed25519 public key encrypted_private_key?: str # AES-256-GCM encrypted private key keypair_version: i32 # Keypair version number subscription_plan?: str # Subscription plan: 6_months, 1_year, 2_years, 3_years auto_renewal: bool # Whether auto-renewal is enabled subscription_start_date?: otime # When the current subscription period started subscription_end_date?: otime # When the current subscription period ends # Payment receiving setup stripe_connected_account_id?: str # Stripe Connect account ID for receiving payments clickpesa_merchant_id?: str # ClickPesa merchant ID for receiving payments payment_receiving_enabled: bool # True once payment receiving is verified # Forward-compat fields (session 67, D-025 per D-024 invariant 6). No M1 logic reads # these; they ship Option-None on every row so M2 (#345) and M3 (#346) don't backfill. # Capture mechanism for the first three is pending Alex clarification ([NEEDS-ALEX-Q23/24/25] # in D-025) — schema slot ships now to avoid a migration when the capture path resolves. is_tanzanian_citizen?: bool # Tax Code Art. 7(c) + Art. 17 double-tax-credit branch (M3 reads) physical_residence_zanzibar?: bool # Tax Code Art. 6 (183-day test) + Art. 7(a)(i)/(ii) rate selection (M3 reads) risk_tier?: RiskTier # iDenfy AML score bucket per Compliance WP Annexe B (M2 ComplianceReview reads) kyc_refreshed_at?: otime # Set on initial KYC + each refresh (M2 cron reads) kyc_next_refresh_at?: otime # kyc_refreshed_at + tier_interval per WP §3.C (M2 cron scans) created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Dividend distribution [rootobject] DividendDistribution = { sid: str # SmartID for storage (dividend_id) company_id: str @index # Reference to FreeZoneCompany total_amount: f64 # Total amount to distribute currency: str # Currency code source_account_id: str # Treasury account to pay from distribution_date: otime # When dividends will be paid status: DividendStatus # Proposed, Approved, Processing, Completed, Cancelled requires_vote: bool # Whether distribution requires vote proposal_id?: str # Reference to Proposal if vote required initiated_by: str # Resident ID who initiated approved_at?: otime # When distribution was approved completed_at?: otime # When distribution completed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Individual dividend payment [rootobject] DividendPayment = { sid: str # SmartID for storage (payment_id) distribution_id: str @index # Reference to DividendDistribution company_id: str @index # Reference to FreeZoneCompany recipient_id: str @index # Recipient resident ID ownership_percentage: f64 # Ownership % at time of distribution amount: f64 # Amount to receive currency: str # Currency code destination_account_id?: str # Treasury account to receive funds transaction_id?: str # Reference to TreasuryTransaction paid_at?: otime # When payment was made created_at: otime # Creation timestamp } # DocumentAudience — the visibility primitive from FREEZONE.md principle 2. # Records *what* a document is (classification + relevant-period window) so that # `can_view(subject_id, document_ref)` can compute "may this subject read this # document right now?" from (CompanyRole rows × classification) on demand. # # Principals are deliberately NOT stored — computing them from CompanyRole on every # read is what keeps the answer coherent as roles change (principle 3: past parties # retain view for their period of interest; future parties do not gain retroactive view). # # Append-only like CompanyRole: if the document's classification or relevant-period # changes, write a new row with `supersedes` pointing at the prior sid. Never edit in # place. # # Relevant-period semantics (the principle-3 invariant): # - document_relevant_from: usually = document.created_at / signed_at / issued_at # - document_relevant_to: None = document is still live; Some(t) = closed at t # (supersession, termination, cancellation). `can_view` # checks whether a CompanyRole's (valid_from, valid_to) # window OVERLAPS this window — so a past party who held # the role during any part of the document's live period # retains view, and a future party who joined after the # document closed does NOT gain view. # [rootobject] DocumentAudience = { sid: str # SmartID for storage document_ref: str @index # Formatted document ID (type-prefix discriminated) document_type: DocumentKind @index # Which OSIS entity the ref points at company_id: str @index # Formatted company ID the document belongs to classification: [str] # Audience tiers this doc is visible to # (symmetric to CompanyRole.classification_scope: # view iff non-empty intersection) audience_computation: str # Rule identifier for audit trail # (e.g. "shareholders_at_signing", "coop_members_at_issue") document_relevant_from: otime # Inclusive start of document's live window document_relevant_to?: otime # Exclusive end; None = document is still live as_of_epoch: otime # When this audience row was computed supersedes?: str # sid of prior DocumentAudience row this replaces created_at: otime created_by: str # Formatted ID of the actor who wrote this row } # Email verification token [rootobject] EmailVerificationToken = { sid: str # SmartID for storage user_id: str @index # Reference to User token_hash: str @index # sha256(raw verification token) expires_at: otime # Token expiration used: bool # Whether token was used created_at: otime } # ─────────────────────────────────────────────────────────────────────────────── # ENTITY CREATION — Multi-sig draft + invite/countersign (D2) # ─────────────────────────────────────────────────────────────────────────────── # Entity draft — a company or cooperative being formed, before all parties sign [rootobject] EntityDraft = { sid: str entity_type: str @index # "company" or "cooperative" entity_name: str @index # Company/coop name company_type: CompanyType # startup_fzc, growth_fzc, global_fzc, coop_standard_fzc, etc. creator_user_id: str @index # Founder's user SID creator_dr_id: str # Founder's DR SID template_data: str # JSON: filled form fields from dynamic schema rendered_documents: str # JSON: array of {contract_id, name, content} shareholders: [ShareholderData] # Share allocation (companies) or founding members (coops) status: EntityDraftStatus # draft → pending_signatures → all_signed → paid → active document_version: i32 # Increments on edit, invalidates signatures signature_deadline: otime # Set by founder (default: 30 days) total_required_signatures: i32 # How many people must sign completed_signatures: i32 # How many have signed so far form_schema_id?: str # Reference to form schema used (for audit) created_at: otime finalized_at?: otime # When draft was finalized (invites sent) completed_at?: otime # When all signatures collected paid_at?: otime # When payment confirmed } # Invitation to join an entity as shareholder or founding member [rootobject] EntityInvitation = { sid: str draft_id: str @index # Reference to EntityDraft entity_name: str # Company/coop name (denormalized for display) inviter_user_id: str @index # Internal SID — DO NOT render to UI inviter_dr_id?: str # Inviter's formatted DR id (ZDFZ-2026-RA…) — denormalized for display inviter_name?: str # Inviter's full name — denormalized for display invitee_user_id: str @index # Internal SID — DO NOT render to UI invitee_email: str @index # For lookup + email notification invitee_dr_id: str # Invited person's formatted DR id role: str # "shareholder", "founding_member", "director" share_percentage: f64 # Ownership % (0 for coops) status: ShareholderInviteStatus # pending | accepted | rejected | expired document_version: i32 # Which draft version they signed (must match) signature_id?: str # ContractSignature SID after signing invited_at: otime responded_at?: otime expires_at: otime # = draft.signature_deadline } # Crypto exchange transaction [rootobject] ExchangeTransaction = { sid: str # SmartID for storage (exchange_id) owner_type: EntityType # Resident or company owner_id: str @index # Owner ID from_account_id: str @index # Source account to_account_id: str @index # Destination account from_amount: f64 # Amount being exchanged from_currency: str # Source currency to_amount: f64 # Amount received to_currency: str # Destination currency exchange_rate: f64 # Exchange rate used fee_percentage: f64 # Fee percentage fee_amount: f64 # Fee amount in from_currency status: ExchangeStatus # Pending, Processing, Completed, Failed, Expired rate_expires_at: otime # When quoted rate expires initiated_by: str @index # Resident ID who initiated completed_at?: otime # When exchange completed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # ─────────────────────────────────────────────────────────────────────────────── # FINANCIAL REPORTS # ─────────────────────────────────────────────────────────────────────────────── # Generated financial report [rootobject] FinancialReport = { sid: str # SmartID for storage (report_id) company_id: str @index # Reference to FreeZoneCompany report_type: FinancialReportType # Balance sheet, Income statement, Cash flow, Trial balance title: str @index # Report title period_start?: otime # Start of reporting period period_end?: otime # End of reporting period as_of_date?: otime # Point-in-time date (for balance sheet) content: str # Report content (JSON or markdown) generated_by: str # Resident ID who generated created_at: otime # Creation timestamp } # Free Zone Company entity [rootobject] FreeZoneCompany = { sid: str # SmartID for storage (internal ID) company_id: str @index # Formatted ID: ZDFZ-2026-YA-PA######, Y for company, P for coop (rollover at 999,999) user_id: str @index # Owner user reference (always stamped; partner-onboarded companies use PartnerConfig.wallet_user_id) # Partner that onboarded this company (π-2a, D-019, s61). None for freezone-native companies. # Used with user_id as an ownership tuple — see DigitalResident.partner_id. partner_id?: str @index # Forward-compat field (session 67, D-025 per D-024 invariant 6). Populated when the # introducing partner is a Venture Creator per Tax Code Art. 7(a)(iv) (0%-for-10-years # window). No M1 logic reads this; ships Option-None on every row. Depends on a M2 # extension to PartnerConfig (venture_creator: bool flag — not yet present; see #332). venture_creator_id?: str # PartnerConfig key that introduced the company iff that partner is a Venture Creator name: str @index # Company name company_type: CompanyType # Type of company voting_model: VotingModel # Standard (by shares) or Cooperative (1 vote each) description: str @index # Company description registration_number: str # Official registration number tax_id: str # Tax ID address: Address # Company address (embedded) shareholders: [ShareholderData] # Rich shareholder data crypto_wallets: [CryptoWallet] # Company crypto wallets status: CompanyStatus # Company status email?: str @index # Contact email (indexed for read-time Contact internal_kind resolution — home#412) phone?: str # Contact phone website?: str # Company website URL industry?: str # Business sector public_key?: str # Ed25519 public key encrypted_private_key?: str # AES-256-GCM encrypted private key keypair_version: i32 # Keypair version number # Subscription fields subscription_plan?: CompanySubscriptionPlan # Monthly, 1yr, or 2yr subscription_start_date?: otime # When subscription started subscription_end_date?: otime # When subscription ends auto_renewal: bool # Whether to auto-renew # Pricing snapshot at registration setup_fee_paid: f64 # Setup fee paid (after discounts) monthly_fee_rate: f64 # Base monthly fee rate shareholder_fee_rate: f64 # Per-shareholder fee rate (0 if N/A) prepaid_discount_applied: f64 # Discount percentage applied (0.0, 0.2, or 0.4) early_bird_applied: bool # Whether early bird discount was applied draft_id?: str # Reference to EntityDraft (set during multi-sig creation) # Payment receiving setup stripe_connected_account_id?: str # Stripe Connect account ID for receiving payments clickpesa_merchant_id?: str # ClickPesa merchant ID for receiving payments payment_receiving_enabled: bool # True once payment receiving is verified # Governance due-process executor config (session 55). Coop variants only; None falls # back to branding.toml [governance.due_process.default_executors] at handler time. due_process_executors?: CoopDueProcessExecutors created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Free Zone Expense entity [rootobject] FreeZoneExpense = { sid: str # SmartID for storage (fz_expense_id) fzc_id: str @index # Reference to FreeZoneCompany receipt_number: str @index # Receipt number vendor_name: str @index # Vendor name description: str @index # Expense description amount: f64 # Expense amount currency: str # Currency code category: str # Expense category receipt_url?: str # URL to receipt image expense_date: otime # Expense date status: ExpenseStatus # Expense status created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Free Zone Invoice entity [rootobject] FreeZoneInvoice = { sid: str # SmartID for storage (fz_invoice_id) fzc_id: str @index # Reference to FreeZoneCompany reseller_invoice_id: str # Reference to reseller system invoice_number: str @index # Invoice number for display amount: f64 # Total invoice amount currency: str # Currency code (USD, EUR, etc.) tax_amount: f64 # Tax amount description: str @index # Invoice description line_items: [LineItem] # List of invoice items issue_date: otime # Issue date due_date: otime # Due date status: InvoiceStatus # Invoice status sender_user_id?: str @index # User who created/sent the invoice (P2P) sender_entity_type?: str # "company" or "cooperative" (null = DR) sender_entity_id?: str @index # FZC/coop SID if sending on behalf of entity (D-043: indexed for sender-entity invoice lookup) recipient_user_id?: str @index # User who should pay the invoice (P2P) recipient_entity_type?: str # "resident" or "company" or "cooperative" recipient_entity_id?: str @index # DR ID or Company/Coop ID (D-043: indexed for recipient-entity invoice lookup) recipient_email?: str @index # External recipient email (D-043: indexed for find_by_email path) payment_method?: str # "stripe" or "clickpesa" (set on payment) payment_reference?: str # Stripe payment_intent ID or ClickPesa ref paid_at?: otime # When payment was completed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Frontend error report — captured from user-facing app [rootobject] FrontendError = { sid: str # SmartID for storage user_id: str @index # User who hit the error (empty if pre-login) error_code: str @index # Error category (network, auth, validation, server, etc.) page: str @index # Page/route where error occurred message: str # Error message shown to user user_agent: str # Browser user-agent string ip_address: str # Client IP (for rate limiting dedup) created_at: otime # When the error occurred } # ─────────────────────────────────────────────────────────────────────────────── # TREASURY OPERATIONS # ─────────────────────────────────────────────────────────────────────────────── # Fund transfer between accounts [rootobject] FundTransfer = { sid: str # SmartID for storage (transfer_id) from_account_id: str @index # Source treasury account to_account_id?: str @index # Destination treasury account (internal) to_external_address?: str # External bank/wallet address to_external_name?: str # External recipient name amount: f64 # Transfer amount currency: str # Currency code reference?: str # Payment reference status: FundTransferStatus # Pending, Processing, Completed, Failed, Cancelled requires_approval: bool # Whether transfer needs approval approval_threshold: f64 # Amount above which approval is needed approved_by?: str # Resident ID who approved initiated_by: str @index # Resident ID who initiated transaction_id?: str # External transaction ID fee_amount?: f64 # Transfer fee completed_at?: otime # When transfer completed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # GovernanceAction — coop member discipline due-process step (session 55, #288, Alex # Q3 resolved 2026-04-22 at #308#22569). The coop-member-discipline equivalent of the # shareholder-voting flow (Proposal/Vote/Resolution). Orthogonal primitives — do NOT # merge: Proposal/Vote/Resolution = voting mechanics; GovernanceAction = 5-step due # process for member discipline. # # Each of the 5 Bylaws steps is a new row chained via parent_action_sid: # notice → response → review → decision → appeal # An appeal RULING is itself a `decision` row with parent_action_sid = appeal_sid; # the parent's kind discriminates which executor set applies (Step-4 for review- # parented decisions; Step-5 for appeal-parented decisions). # # Append-only (principle 3): never mutate a row. decided_at / decided_by / # outcome are populated at write time for kind=decision rows and never change # afterwards. Enforcement of `outcome=remove` is a SEPARATE handler # (`enforce_decision`) that calls `companyroleservice.remove_member` and writes a # single UserAuditLog row — it does NOT mutate the decision row and does NOT write # a new GovernanceAction row. This was a B.5 catch (see decisions/013): # - Collapsing closure into `issue_decision` would cross the OSIS multi-entity # transaction boundary — partial failure leaves a decision row with no paired # closure, and append-only forbids re-trying the decision. # - Opportunistic-on-next-call closure would leak cross-subject side effects # (filing a notice on member B could close member X's role as a side effect of # a handler that never mentioned X). # Therefore enforcement is an explicit, idempotent, caller-authorized handler; # D-011's monotonic "already-closed" guard on CompanyRole makes retry a no-op. # [rootobject] GovernanceAction = { sid: str # SmartID for storage company_id: str @index # Formatted coop ID (ZDFZ-YYYY-YP... or ZDFZ-YYYY-P...) subject_resident_id: str @index # Formatted DR ID of the member under discipline kind: GovernanceActionKind @index # Which step in the 5-step chain parent_action_sid?: str @index # Prior step in chain; None iff kind=notice content: str # Free-form JSON-serialized step content due_by: otime # When this step's SLA expires (computed at write # from prior step's due_by + branding.toml SLA) decided_at?: otime # When the ruling was issued (kind=decision only) decided_by_role?: ExecutorRole # Executor set the ruler belonged to (decision only) decided_by_subject_id?: str # Formatted ID of the individual who ruled (decision only) outcome?: DecisionOutcome # The ruling (kind=decision only; None otherwise) created_at: otime created_by: str # Formatted ID of the actor who wrote this row } # Session 63 (D-021): partner-track π-4 delegated-KYC attestation row (IMMUTABLE side). # Carries the audit-significant metadata: partner identity, partner-supplied signature, # SHA-256 hash over the encrypted payload, encryption scheme label, and key_id used. # Principle 3 immutable — never edited in place, never scrubbed. Paired 1:1 with a # KycArtifactPayload row (separately lifecycled; scrubbable for GDPR Art. 17). The # attestation outlives the payload: after erasure, the attestation + payload_hash still # prove "partner P attested to artifact H at time T" even if the plaintext is gone. # RegistrationRequest.kyc_attestation_sid points HERE, never at the payload. # Signature is stored verbatim in M1 — never verified (no partner-pubkey infra yet). # See decisions/021-partner-track-kyc-artifact.md. [rootobject] KycArtifactAttestation = { sid: str # SmartID for storage request_id: str @index # Reference to RegistrationRequest.sid partner_id: str @index # Partner name (e.g. "tftc") — denormalized from RegistrationRequest at submit partner_signature: str # Partner's signed attestation string — STORED VERBATIM, never verified in M1 (same pattern as s62 synthetic admin_signature). M2 adds partner-pubkey + Ed25519 verify. payload_hash: str @index # SHA-256 hex over the encrypted_payload bytes at submit time. Integrity check survives payload erasure — auditors can verify "hash H matches what partner attested to" even after the blob is scrubbed. encryption_scheme: str # Label identifying the encryption format (M1: "aes-256-gcm-v1"). Future-proofs for when X25519 asymmetric lands in M2. key_id: str @index # Which partner artifact_key was used (from [partner_config.].artifact_keys map). Survives key rotation — pre-rotation artifacts stay decryptable as long as the historical key is retained. created_at: otime # Creation timestamp } # Session 63 (D-021): partner-track π-4 delegated-KYC payload row (ERASABLE side). # Carries the PII: the encrypted payload itself. Separately lifecycled from the # attestation so a GDPR Art. 17 right-to-erasure can scrub `encrypted_payload` + stamp # `erased_at` + `erasure_reason` without editing the principle-3 immutable attestation. # When erased: encrypted_payload becomes empty string + erased_at is Some; the attestation # survives unmodified. Admin read-path (π-5, #331) branches on `erased_at.is_some()`: # on erased rows, surface a tombstone + erasure_reason instead of attempting decrypt. # Erasure RPC itself is M2 (no admin UI yet); schema shape supports it TODAY so no # principle-3 migration is needed when the RPC lands. # See decisions/021-partner-track-kyc-artifact.md. [rootobject] KycArtifactPayload = { sid: str # SmartID for storage attestation_sid: str @index # Reference to KycArtifactAttestation.sid (1:1) encrypted_payload: str # AES-256-GCM ciphertext (hex(nonce):hex(ct) per existing main.rs::decrypt_aes256gcm format). Partner encrypts CLIENT-SIDE with PartnerConfig.artifact_keys[key_id]; freezone stores verbatim. Empty-string when erased. key_id: str @index # Redundant with attestation.key_id but indexed here too so payload-only lookups work during rotation sweeps. erased_at?: otime # When the payload was scrubbed under GDPR / retention policy. None for live artifacts. erasure_reason?: str # Human-readable reason ("gdpr_erasure" | "retention_expired" | "refused_application" | free-form). None for live artifacts. created_at: otime # Creation timestamp } # KYC Personal Data — verified iDenfy PII fields, encrypted at rest [rootobject] # https://forge.ourworld.tf/znzfreezone_code/home/issues/387 # Captured per iDenfy webhook when [features.kyc_persistence] enabled. Master + per-field # toggles in branding.toml. Optional fields stored as None when toggled off; existing rows # stay sparse on subsequent toggle-on (no backfill — refresh-cadence scheduler is umbrella #386). KycPersonalData = { sid: str # SmartID for storage resident_id: str @index # Reference to DigitalResident user_id: str @index # Reference to User kyc_provider_id: str # iDenfy scan_ref (denormalized for audit) dob?: str # Date of birth (encrypted at rest when encrypt_at_rest=true) nationality?: str # Nationality (encrypted at rest) document_number?: str # Document number (encrypted at rest) document_type?: str # Document type (encrypted at rest) document_expiry?: str # Document expiry (encrypted at rest) created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # KYC Session entity [rootobject] KycSession = { sid: str # SmartID for storage (session_id) user_id: str @index # Reference to User resident_id: str @index # Reference to DigitalResident public_key: str # Public key used for verification kyc_url: str # URL to KYC service status: KycStatus # Verification status idenfy_scan_ref?: str @index # iDenfy scan reference (D-043: indexed for kycsession_find scan_ref lookup) identity_verified: bool # Identity verification result document_verified: bool # Document verification result liveness_verified: bool # Liveness check result verification_level?: str # Verification level (basic, full, etc.) callback_url?: str # Callback URL for completion expires_at: otime # Session expiration verified_at?: otime # When verification completed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # MCP approval queue entry [rootobject]. # Session 65 adds result_json / decision_reason / execution_error to support the # decision flow: result_json carries the tool's output after ApprovedExecuted so # the MCP caller can poll the row for the answer; decision_reason is the # user-supplied reason on reject; execution_error captures why a dispatch failed # after approve (e.g. "ApiKey revoked between approve and execute" — the row # stays Approved, not ApprovedExecuted, so a later retry is possible once the # underlying condition is fixed). See decisions/023-mcp-tier6-approval.md. McpApproval = { sid: str # SmartID for storage user_id: str @index # Who needs to approve (owner of the API key) api_key_id: str @index # Which key triggered it tool_name: str @index # Requested MCP tool name (e.g. "freezone_shareholderservice_execute_dividend") # Resolved OSIS RPC method (e.g. "shareholderservice.execute_dividend"). Stamped at # approval-creation time from the matched McpTool's `rpc_method`. Used by # execute_approved to dispatch — required because tool_name (MCP format) is NOT # accepted by the OSIS dispatcher, which expects ".". Optional # for forward-compat: legacy rows with rpc_method=None fall back to using # tool_name (current pre-fix behaviour, intentionally still broken — production # is locked per §0 and no legacy rows exist). rpc_method?: str arguments: str # Full arguments JSON (not redacted; replayed at execute time) amount: f64 # Financial amount reason: str # Why approval needed (from needs_approval() at create time) status: McpApprovalStatus @index # pending / approved / approved_executed / rejected / expired requested_at: otime @index # When requested decided_at?: otime # When approved/rejected/expired (stamped on terminal transition) expires_at: otime # Auto-expire (default 1 hour) created_at: otime # Creation timestamp result_json?: str # Tool output JSON after successful execute (present iff status=ApprovedExecuted) decision_reason?: str # User-supplied reason on reject execution_error?: str # Dispatch error after approve (e.g. "ApiKey revoked"); row stays Approved } # MCP call audit log [rootobject] McpCallLog = { sid: str # SmartID for storage api_key_id: str @index # Which API key made the call user_id: str @index # Which user owns the key tool_name: str @index # Tool that was called arguments_summary: str # Sanitized args (no secrets) amount: f64 # Financial amount (0 if non-financial) result_status: str @index # success / error / rejected error_message: str # Error detail if rejected/failed created_at: otime @index # Timestamp } # (usually == subject_id; may differ when admin/system acts on behalf) # Password reset token [rootobject] PasswordResetToken = { sid: str # SmartID for storage token_hash: str @index # sha256(raw reset token) email: str @index # User email expires_at: i64 # Unix epoch expiry used: bool # One-time use flag created_at: otime } # Payment entity for crypto/ClickPesa [rootobject] Payment = { sid: str # SmartID for storage (payment_id) user_id: str @index # Reference to User entity_id: str @index # Resident or Company ID entity_type: EntityType # Resident or Company (enum) payment_type: PaymentType # Bitcoin, Ethereum, ClickPesa amount: f64 # Amount in currency currency: str # Currency code (USD, etc.) crypto_amount?: str # Formatted crypto amount exchange_rate?: str # Exchange rate at time of payment wallet_address?: str # Destination wallet address sender_address?: str # Sender's address transaction_hash?: str # Blockchain transaction hash confirmation_count?: i32 # Block confirmations external_id: str # External payment provider ID clickpesa_order_id?: str @index # ClickPesa order ID (indexed for webhook lookup) payment_purpose: str # Purpose (resident_subscription, company_registration) subscription_plan?: str # Subscription plan if applicable auto_renewal: bool # Whether auto-renewal was requested status: PaymentStatus # Payment status expires_at: otime # Payment expiration confirmed_at?: otime # When payment was confirmed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # ─────────────────────────────────────────────────────────────────────────────── # GOVERNANCE & VOTING # ─────────────────────────────────────────────────────────────────────────────── # Governance proposal [rootobject] Proposal = { sid: str # SmartID for storage (proposal_id) company_id: str @index # Reference to FreeZoneCompany title: str @index # Proposal title description: str # Full proposal description (markdown) category: ProposalCategory # Financial, Strategic, Operational, Governance, Personnel kind: ResolutionKind @index # Ordinary, Special, or Board — drives threshold comparator (session 56 / D-014) status: ProposalStatus # Draft, Active, Passed, Rejected, Expired, Executed proposer_id: str @index # Resident ID who created proposal voting_starts_at: otime # When voting begins voting_ends_at: otime # When voting ends quorum_percentage: i32 # Required participation percentage (0-100) — snapshotted from branding.toml at creation per kind passing_threshold: i32 # Required "for" percentage to pass (0-100) — snapshotted from branding.toml at creation per kind total_issued_power_snapshot: f64 # Total issued voting power at create_proposal time (session 56 / D-014). Standard = sum of ShareholderData.percentage; Cooperative = count of open founding_member + platinum_member CompanyRoles. Denominator for quorum check at tally time. Frozen by proposal.set immutability guard. allow_abstention: bool # Whether abstention counts toward quorum (NOT toward passing threshold per Alex Q2 "aligned with M&A") votes_for: f64 # Total voting power for votes_against: f64 # Total voting power against votes_abstain: f64 # Total voting power abstained total_voted: f64 # Total voting power that voted resolution_id?: str # Reference to Resolution if executed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Historical public key for signature verification [rootobject] PublicKeyHistory = { sid: str # SmartID for storage user_id: str @index # Reference to User entity_type: EntityType # Which entity this key belonged to entity_id: str @index # Resident or Company ID public_key: str # The historical public key keypair_version: i32 # Version number of this key valid_from: otime # When this key became active valid_until: otime # When this key was replaced created_at: otime # Record creation timestamp } # Registration Contract entity [rootobject] # Note: category_str is indexed for Tantivy search; category enum provides type safety RegistrationContract = { sid: str # SmartID for storage (contract_id) name: str @index # Contract name version: str # Contract version content: str # Full contract text content_hash: str # SHA-256 hash of content required: bool # Must be signed category: RegistrationContractCategory # Contract category (enum for type safety) category_str: str @index # Category as string for Tantivy search (e.g., "digital_residency") created_at: otime # Creation timestamp } # Reseller registration request for admin approval [rootobject] RegistrationRequest = { sid: str # SmartID for storage reseller_id: str @index # API key / reseller user ID request_type: RegistrationRequestType # resident or company client_email: str @index # Client's email address client_name: str @index # Client's full name kyc_session_id?: str @index # Reference to KycSession payment_id?: str @index # Reference to Payment signed_contract_ids: [str] # References to ContractSignature sids status: RegistrationRequestStatus # Current workflow status rejection_reason?: str # Reason if rejected admin_notes?: str # Internal admin notes submitted_at: otime # When request was submitted reviewed_at?: otime # When admin reviewed reviewed_by?: str @index # Admin email who reviewed reseller_signature: str # Reseller's digital signature admin_signature?: str # Admin's co-signature (on approval) partner_id?: str @index # s62: partner name (denormalized from caller_partner_id at submit) trust_tier?: str @index # s62: captured-at-submit trust tier ("auto" | "manual") — locks decision against mid-flight config flips kyc_attestation_sid?: str @index # s63 (D-021): ref to KycArtifactAttestation.sid (the IMMUTABLE side). Never points at the payload — GDPR erasure scrubs the payload row while the attestation survives for audit continuity. Auto-tier verify_prereqs bypass requires this to be Some. # Issue #377 (partner-types): closes the M1 partner-API/portal symmetry gap. Required when # request_type=Company; ignored when request_type=Resident. company_type defaults to SingleFzc # at the helper level for backward compat with pre-#377 partner submits (which had no field). company_type?: CompanyType # Issue #377: company type (SingleFzc | StartupFzc | ... | CoopPlatinumFzc) shareholders?: [ShareholderData] # Issue #377: required for non-SingleFzc types; SingleFzc auto-resolves via client_email subscription_plan?: CompanySubscriptionPlan # Issue #377: monthly | one_year | two_years; helper defaults to Monthly when None auto_renewal?: bool # Issue #377: stamped on the materialized Company; helper defaults to false when None created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Executed resolution from passed proposal [rootobject] Resolution = { sid: str # SmartID for storage (resolution_id) company_id: str @index # Reference to FreeZoneCompany proposal_id: str @index # Reference to original Proposal title: str @index # Resolution title description: str # Resolution description category: ProposalCategory # Category from proposal votes_for: f64 # Final votes for votes_against: f64 # Final votes against votes_abstain: f64 # Final abstentions quorum_reached: bool # Whether quorum was reached passed: bool # Whether resolution passed executed_at?: otime # When resolution was executed executed_by?: str # Who executed the resolution execution_notes?: str # Notes about execution created_at: otime # Creation timestamp } # Share transfer between shareholders [rootobject] ShareTransfer = { sid: str # SmartID for storage (transfer_id) company_id: str @index # Reference to FreeZoneCompany from_resident_id: str @index # Seller resident ID to_resident_id: str @index # Buyer resident ID percentage: f64 # Percentage being transferred reason?: str # Reason for transfer status: ShareTransferStatus # Pending, Approved, Rejected, Completed, Cancelled requires_vote: bool # Whether transfer requires shareholder vote proposal_id?: str # Reference to Proposal if vote required from_signature?: str # Seller's digital signature to_signature?: str # Buyer's digital signature approved_at?: otime # When transfer was approved completed_at?: otime # When transfer completed created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # ─────────────────────────────────────────────────────────────────────────────── # SHAREHOLDER MANAGEMENT # ─────────────────────────────────────────────────────────────────────────────── # Invitation to become shareholder [rootobject] ShareholderInvite = { sid: str # SmartID for storage (invite_id) company_id: str @index # Reference to FreeZoneCompany inviter_id: str @index # Resident ID who sent invite invitee_email: str @index # Email of invitee invitee_resident_id?: str @index # Resident ID if already a resident proposed_percentage: f64 # Proposed ownership percentage message?: str # Personal message status: ShareholderInviteStatus # Pending, Accepted, Rejected, Expired token: str @index # Unique invite token expires_at: otime # When invite expires responded_at?: otime # When invitee responded created_at: otime # Creation timestamp } # SigningAuthority — delegated signing grant per M&A Art. 3 + Alex Q1 (2026-04-22, # #308#22569). Stores ONLY delegated grants (granted by Board Resolution, director-level). # # Director-auto signing is NOT stored as a SigningAuthority row. A subject holding a # currently-open `CompanyRole { role=Director, company_id, subject_id }` is authorised # to sign for that company by virtue of the Director role itself — `can_sign` computes # director-auto at query time from CompanyRole directly (the primitive that already # carries role × time × classification). This was a deliberate B.5 catch: # # - Storing a redundant `director_auto` row alongside the Director CompanyRole would # introduce a dual-write surface with no OSIS multi-entity transaction — a partial # failure could leave an open Director row with no paired grant row, silently # breaking can_sign for a seated director (principle 2 violation). # - One concept, one helper (CLAUDE.md): an open Director CompanyRole already answers # "is this subject authorised to sign for this company by virtue of role?". A second # store representing the same fact is a class violation. # # Therefore: SigningScopeKind enum is NOT introduced. Every SigningAuthority row is, # by construction, a bounded-scope delegation with `granted_by_resolution` REQUIRED. # See decisions/012-signing-authority.md. # # Append-only per principle 3. Closure via D-011 in-place `valid_to: None → Some(ts)` # mutation (canonical closure primitive; revocation is not a new row). sid + all content # fields immutable after write; only the temporal boundary flips, monotonic write-once. # [rootobject] SigningAuthority = { sid: str # SmartID for storage company_id: str @index # Formatted company ID subject_id: str @index # Formatted subject resident ID amount_ceiling?: f64 # $-cap on documents signable under this grant; None = unbounded category?: str # Document-category scope (e.g. "hr_agreement"); None = any category granted_by_resolution: str # Board Resolution sid (director-level per Alex Q1). Required. valid_from: otime @index # Inclusive — grant is in effect from this instant valid_to?: otime # Exclusive — revoked at this instant (None = currently granted) created_at: otime created_by: str # Formatted ID of the actor who wrote this row } # Tax document [rootobject] TaxDocument = { sid: str # SmartID for storage (doc_id) filing_id: str @index # Reference to TaxFiling company_id: str @index # Reference to FreeZoneCompany document_type: str @index # Type of tax document name: str @index # Document name content?: str # Document content (for generated docs) file_url?: str # URL if uploaded generated: bool # Whether auto-generated created_at: otime # Creation timestamp } # ─────────────────────────────────────────────────────────────────────────────── # TAX & COMPLIANCE # ─────────────────────────────────────────────────────────────────────────────── # Tax filing record [rootobject] TaxFiling = { sid: str # SmartID for storage (filing_id) company_id: str @index # Reference to FreeZoneCompany filing_type: TaxFilingPeriod # Monthly, Quarterly, Annual period_start: otime # Start of filing period period_end: otime # End of filing period due_date: otime # Filing deadline status: TaxFilingStatus # Draft, Submitted, Accepted, Rejected, Amended gross_revenue: f64 # Total revenue cost_of_goods_sold: f64 # COGS gross_profit: f64 # Gross profit operating_expenses: f64 # Operating expenses net_income: f64 # Net income before tax tax_rate: f64 # Applicable tax rate (0% for FreeZone) tax_due: f64 # Tax amount due submitted_at?: otime # When filing was submitted submitted_by?: str # Resident ID who submitted signature?: str # Digital signature created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Treasury Account entity [rootobject] TreasuryAccount = { sid: str # SmartID for storage owner_type: EntityType # Resident or Company (enum) owner_id: str @index # Resident or Company ID name: str @index # Display name kind: TreasuryAccountKind # crypto_wallet or bank_account currency: str # Currency code balance: f64 # Current balance bank_name?: str # For bank accounts iban?: str # International bank account number account_number?: str # Account number bic?: str # Bank identifier code wallet_address?: str # For crypto wallets network?: str # Blockchain network created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Treasury Transaction entity [rootobject] TreasuryTransaction = { sid: str # SmartID for storage account_id: str @index # Reference to TreasuryAccount timestamp: otime # Transaction timestamp tx_type: TreasuryTransactionType # Credit, debit, or transfer (enum) asset: str # Asset type amount: f64 # Transaction amount currency: str # Currency code counterparty?: str # Other party status: TreasuryTransactionStatus # Transaction status (enum) created_at: otime # Creation timestamp } # ─────────────────────────────────────────────────────────────────────────────── # BUSINESS RULES - Implemented in rpc.rs (not supported by new generator) # ─────────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────── # ROOT OBJECTS - Storable entities with CRUD operations [rootobject] # ─────────────────────────────────────────────────────────────────────────────── # User account for authentication [rootobject] User = { sid: str # SmartID for storage email: str @index # Email address (unique) password_hash: str # Argon2id hashed password first_name?: str @index # First name last_name?: str @index # Last name is_active: bool # Account active status email_verified: bool # Email verification status email_verified_at?: otime # When email was verified kyc_verified: bool # KYC verification status kyc_verified_at?: otime # When KYC was verified dr_contracts_signed: bool # Has signed all required DR contracts (home#446) dr_contracts_signed_at?: otime # When the DR contracts threshold was crossed public_key?: str # Ed25519 public key for signing encrypted_private_key?: str # AES-256-GCM encrypted private key keypair_version: i32 # Keypair version number created_at: otime # Account creation timestamp updated_at: otime # Last update timestamp } # User AI agent configuration [rootobject] UserAIConfig = { sid: str # SmartID for storage user_id: str @index # Reference to User (one config per user) ai_provider: AiProvider # groq, openrouter, sambanova, deepinfra ai_api_key_encrypted: str # AES-256-GCM encrypted provider API key ai_model: str # Model ID (e.g., "llama-3.3-70b", "claude-sonnet-4-20250514") freezone_api_key_id?: str # Reference to user's PAT (ApiKey sid) is_active: bool # Whether AI agent is configured and active created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # UserAuditLog — typed append-only observation of every audited action. # Session 49 seed of FREEZONE.md principle 4 ("typed actors") audit trail requirement. # # Append-only by SERVICE SHAPE, not just convention: UserAuditLogService declares # `log` + `list_for_*` only. No `update_*`, no `delete_*`. Corrections of a bad row # are an ORTHOGONAL new entity (future `UserAuditLogCorrection`) — never a mutation # of the original, never a `supersedes` pointer on this entity. # # Session 50 adds an optional typed `visibility: VisibilityDecision?` sub-record # populated ONLY for `action = view_document` rows. Non-visibility audit rows (the # session-49 4 wired sites: sign_contract, renew_subscription, verify_kyc, grant_bonus) # leave `visibility = None`. A plain `audience_computation: str` at top level was # rejected in session 49 because it would either (a) overload JSON on every visibility # row, or (b) remain dead weight on every non-visibility row forever. The typed # sub-record is the right shape. # # Deliberately NO `supersedes?` field: carrying it would contradict the append-only # stance and invite future correction workflows that belong in a separate entity. # # Timing: `log()` is called AFTER the action resolves (success OR recoverable # failure). A handler that crashes mid-action leaves no audit row. Audit-before # would leave phantom rows for actions that never happened; audit-after captures # what actually happened. Rust panics surface through tracing already. # [rootobject] UserAuditLog = { sid: str # SmartID for storage subject_id: str @index # Formatted actor ID (ZDFZ-2026-RA…) or "system" action: AuditAction @index # Which action was observed target_ref: str @index # Formatted ID of acted-upon entity (empty if N/A) target_type: AuditTargetType # What kind of entity target_ref points at allowed: bool # True = action succeeded; false = denied/validation-fail decided_at: otime # When the action resolved context: str # Free-form JSON-serialized by caller # (request origin, correlation_id, redacted details…) visibility?: VisibilityDecision # Populated ONLY for action=view_document rows # (session 50). None on non-visibility audit rows. created_at: otime created_by: str # Formatted ID of the actor who wrote this row } # User Keypair entity for storing encrypted keys [rootobject] UserKeypair = { sid: str # SmartID for storage user_id: str @index # Reference to User entity_type: EntityType # Resident or Company (enum) entity_id: str @index # Resident or Company ID public_key: str # Ed25519 public key encrypted_private_key: str # AES-256-GCM encrypted private key version: i32 # Keypair version number is_current: bool # Whether this is the current keypair created_at: otime # Creation timestamp } # User message entity [rootobject] UserMessage = { sid: str # SmartID for storage user_id: str @index # Reference to User title: str @index # Message title body: str @index # Message body/content message_type: MessageType # Type (system/notification/alert) category?: MessageCategory # Category action_url?: str # URL for action button action_label?: str # Label for action button is_read: bool # Read status created_at: otime # Creation timestamp updated_at: otime # Last update timestamp } # Verification Link for Tier 3 access [rootobject] VerificationLink = { sid: str # SmartID for storage (link_id) user_id: str @index # Owner user reference resident_id?: str @index # Reference to DigitalResident company_id?: str @index # Reference to FreeZoneCompany token: str @index # Unique verification token label?: str # User-defined label expires_at: otime # Link expiration single_use: bool # Whether link is single-use use_count: i32 # Number of times used status: VerificationLinkStatus # Link status first_used_at?: otime # First usage timestamp last_used_at?: otime # Last usage timestamp created_at: otime # Creation timestamp } # Individual vote on a proposal [rootobject] Vote = { sid: str # SmartID for storage (vote_id) proposal_id: str @index # Reference to Proposal company_id: str @index # Reference to FreeZoneCompany voter_id: str @index # Resident ID of voter vote_value: VoteValue # For, Against, Abstain voting_power: f64 # Voter's voting power at time of vote comment?: str # Optional comment signature: str # Digital signature of vote created_at: otime # When vote was cast } # ─────────────────────────────────────────────────────────────────────────────── # EMBEDDED TYPES - Complex types used within root objects # ─────────────────────────────────────────────────────────────────────────────── AcceptInviteResult = { success: bool company_id: str percentage: f64 } ActivateResult = { activated: bool message: str } # ─────────────────────────────────────────────────────────────────────────────── # EMBEDDED TYPES - Nested structures (no sid field) # ─────────────────────────────────────────────────────────────────────────────── # Physical address Address = { street: str city: str state: str postal_code: str country: str } AdminRegistrationError = { code: str message: str } # All pricing configurations AllPricingConfig = { company_types: [CompanyPricingConfig] resident_monthly_fee: f64 early_bird_discount: f64 # 0.20 = 20% one_year_discount: f64 # 0.20 = 20% two_year_discount: f64 # 0.40 = 40% available_payment_methods: [str] # Which payment methods are active (from branding.toml) min_top_up: f64 # Resolved wallet top-up minimum (branding.toml [credits] min_top_up_usd; TOPUP_MIN_USD env override) } ApiKeyError = { code: str message: str } # API key service types ApiKeyResult = { api_key: str expires_at: otime } # ═══════════════════════════════════════════════════════════════════════════════ # ADDITIONAL RESULT AND DATA TYPES # Added to support generated RPC handlers # ═══════════════════════════════════════════════════════════════════════════════ # API Key service types ApiKeysListResult = { api_keys: [ApiKey] } ApproveRegistrationResult = { request_id: str status: RegistrationRequestStatus created_entity_type: RegistrationRequestType created_entity_id: str approved_at: otime } AuthError = { code: str message: str } # ─────────────────────────────────────────────────────────────────────────────── # SERVICE REQUEST/RESPONSE TYPES # ─────────────────────────────────────────────────────────────────────────────── # Auth service types AuthResult = { token: str # Access token (JWT, short-lived ~15min) refresh_token: str # Refresh token (JWT, long-lived ~7days) token_type: str # "Bearer" expires_in: i64 # Access token expiry in seconds user: UserInfo # User information } # Result of auto top-up configuration AutoTopupResult = { enabled: bool amount: f64 threshold: f64 setup_url?: str } BalanceResult = { account_id: str balance: str currency: str } BusinessDocumentDetailResult = { document_id: str company_id: str document_type: BusinessDocumentType name: str description?: str file_url?: str is_official: bool issued_date?: otime expiry_date?: otime created_at: otime } BusinessDocumentError = { code: str message: str } BusinessDocumentListResult = { documents: [BusinessDocumentDetailResult] } # Business document service types BusinessDocumentResult = { document_id: str document_type: BusinessDocumentType } ChangePasswordResult = { success: bool message: str } ClickPesaResult = { payment_id: str order_id: str payment_url: str status: PaymentStatus expires_at: otime } ClickPesaWebhookData = { event: str order_id: str reference: str amount: f64 currency: str status: str payment_method?: str transaction_id?: str paid_at?: otime checksum?: str } # Company service types CompaniesListResult = { companies: [FreeZoneCompany] total: i32 page: i32 page_size: i32 } CompanyCardResult = { company_id: str name: str company_type: CompanyType status: CompanyStatus established: otime public_key?: str keypair_version?: i32 } # Company service types CompanyCreateData = { name: str company_type: CompanyType resident_id: str trading_name?: str description?: str } CompanyError = { code: str message: str } # Company pricing configuration (for display) CompanyPricingConfig = { company_type: CompanyType setup_fee: f64 monthly_fee: f64 max_shareholders: i32 has_shareholder_fee: bool shareholder_tiers: [ShareholderFeeTier] # Empty if no per-shareholder fee available: bool # Whether this type can be registered (from branding.toml) } CompanyPricingResult = { registration_fee: f64 annual_fee: f64 currency: str company_type: CompanyType } CompanyResult = { company: FreeZoneCompany } CompanyRoleError = { code: str message: str } # Result wrapper for the CompanyRole list RPCs (list_by_company / list_by_resident). CompanyRoleListResult = { roles: [CompanyRole] } CompanyUpdateData = { name?: str trading_name?: str description?: str status?: CompanyStatus } CompanyVerificationResult = { valid: bool company_id: str status: CompanyStatus verified_at: otime has_full_access: bool name?: str company_type?: CompanyType established?: otime public_key?: str keypair_version?: i32 verified_by?: str } ConfirmResetResult = { success: bool } # Contract service types ContractCreateData = { title: str content: str contract_type: str parties: [str] } ContractError = { code: str message: str } ContractResult = { contract: Contract } # Contract service types ContractsListResult = { contracts: [Contract] } # (e.g. "projected_contract_v1", "shareholders_at_signing", # or "no_audience_row" on strict-deny fallback) # Per-coop due-process executor choices (session 55). Coops pick ONE ExecutorRole # per step at formation; absent (None on FreeZoneCompany), handlers fall back to # branding.toml [governance.due_process.default_executors]. Non-coop company_type # variants leave this None unconditionally (governance handlers reject non-coop # company_ids by company_type check, not by executor-config presence). CoopDueProcessExecutors = { step1_notice: ExecutorRole # Who files the initial notice step2_response: ExecutorRole # Who reviews the member's response step3_review: ExecutorRole # Who opens the formal review step4_decision: ExecutorRole # Who issues the ruling step5_appeal: ExecutorRole # Who rules on appeals } CreateCompanyData = { name: str company_type: CompanyType description: str address: Address shareholders: [ShareholderData] email?: str phone?: str website?: str industry?: str # Subscription fields subscription_plan: CompanySubscriptionPlan # Monthly, 1yr, or 2yr auto_renewal: bool # Whether to auto-renew apply_early_bird: bool # Whether to apply early bird discount # Client-generated keypair (private key never leaves the browser) public_key: str # Ed25519 public key (hex) encrypted_private_key: str # AES-256-GCM encrypted private key (base64) } CreateContractData = { name: str contract_type: str counterparty: str start_date: otime end_date?: otime total_value?: f64 } CreateExpenseData = { receipt_number: str vendor_name: str description: str amount: f64 currency: str category: str receipt_url?: str expense_date: otime } CreateInvoiceData = { invoice_number: str amount: f64 currency: str tax_amount: f64 description: str line_items: [LineItem] issue_date: otime due_date: otime } CreateLinkResult = { link_id: str token: str verification_url: str expires_at: otime single_use: bool } CreateResidentData = { email: str first_name: str last_name: str phone: str kyc_provider_id?: str } CreateTreasuryAccountData = { name: str kind: TreasuryAccountKind currency: str bank_name?: str iban?: str account_number?: str bic?: str wallet_address?: str network?: str } # Credit service types (aliases for generator compatibility) CreditBalanceListResult = { balances: [CreditBalance] } # Credit service types CreditBalanceResult = { user_id: str balance: f64 lifetime_earned: f64 lifetime_spent: f64 last_top_up_at?: otime } CreditError = { code: str message: str } CreditPlanInfo = { code: str # Plan code (1_month, 6_months, etc.) label: str # Display label months: i32 # Number of months total_cost: f64 # Total credit cost after discount monthly_effective: f64 # Effective monthly rate discount_percent: f64 # Discount percentage } CreditPricingResult = { kyc_cost: f64 # Cost for KYC verification monthly_subscription: f64 # Monthly subscription cost welcome_bonus: f64 # Credits given on registration currency: str # USD plans: [CreditPlanInfo] # Subscription plans with credit costs } CreditTransactionDetail = { transaction_id: str tx_type: CreditTransactionType amount: f64 balance_after: f64 currency: str description: str reference_type?: str reference_id?: str status: CreditTransactionStatus created_at: otime } CreditTransactionListResult = { transactions: [CreditTransaction] } CreditTransactionResult = { transaction_id: str tx_type: CreditTransactionType amount: f64 balance_after: f64 status: CreditTransactionStatus } CreditTransactionsListResult = { transactions: [CreditTransactionDetail] total: i32 page: i32 page_size: i32 } # Crypto wallet for companies CryptoWallet = { public_key: str # Blockchain address chain: str # Blockchain network (bitcoin, ethereum, etc.) label: str # User-friendly name } DeleteBusinessDocumentResult = { success: bool } DeleteLinksResult = { deleted_count: i64 } DeleteResult = { success: bool message: str } DirectorData = { resident_id: str position: str appointed_date?: otime } DividendDetailResult = { dividend_id: str company_id: str total_amount: f64 currency: str distribution_date: otime status: DividendStatus created_at: otime } DividendDistributionListResult = { distributions: [DividendDistribution] } # Treasury operations types (aliases for generator compatibility) DividendDistributionResult = { distribution: DividendDistribution } DividendListResult = { dividends: [DividendDetailResult] } DividendPaymentListResult = { payments: [DividendPayment] } DividendPaymentResult = { payment: DividendPayment } DividendResult = { dividend_id: str status: DividendStatus } EmailError = { code: str message: str } # Result for get_encrypted_private_key — returns encrypted blob for client-side decryption EncryptedKeyResult = { encrypted_private_key: str # AES-256-GCM encrypted blob (base64), client decrypts keypair_version: i32 } ExchangeDetailResult = { exchange_id: str from_amount: f64 from_currency: str to_amount: f64 to_currency: str exchange_rate: f64 status: ExchangeStatus created_at: otime } ExchangeListResult = { exchanges: [ExchangeDetailResult] } ExchangeQuoteResult = { from_currency: str to_currency: str from_amount: f64 to_amount: f64 exchange_rate: f64 fee_percentage: f64 fee_amount: f64 expires_at: otime } ExchangeResult = { exchange_id: str status: ExchangeStatus } ExchangeTransactionListResult = { exchanges: [ExchangeTransaction] } ExchangeTransactionResult = { exchange: ExchangeTransaction } ExpenseResult = { expense: FreeZoneExpense } # Expense service types ExpensesListResult = { expenses: [FreeZoneExpense] } # ─────────────────────────────────────────────────────────────────────────────── # PRICING TYPES - Company pricing and subscription # ─────────────────────────────────────────────────────────────────────────────── # Fee breakdown for a single fee component FeeBreakdown = { base: f64 # Base amount before discounts months: i32 # Number of months (for recurring fees) quantity: i32 # Quantity (e.g., shareholder count) subtotal: f64 # base * months * quantity discount: f64 # Discount amount discount_label: str # e.g., "Early Bird (20%)" or "1 Year Prepaid (20%)" final: f64 # Final amount after discount } FinancialReportDetailResult = { report_id: str company_id: str report_type: FinancialReportType title: str period_start?: otime period_end?: otime as_of_date?: otime content: str created_at: otime } FinancialReportListResult = { reports: [FinancialReportDetailResult] } # Report service types FinancialReportResult = { report_id: str report_type: FinancialReportType } # Dynamic form field definition for entity creation wizards FormFieldDefinition = { key: str # Field key (used in template_data JSON and as template placeholder) field_type: FormFieldType # text, textarea, dropdown, number, checkbox, multi_select label: str # Display label for UI description?: str # Help text / instructions required: bool # Whether field must be filled options: [FormFieldOption] # Choices for dropdown/multi_select (empty for other types) default_value?: str # Default value (string representation) min_value?: f64 # Minimum for number fields max_value?: f64 # Maximum for number fields group?: str # Wizard step/section grouping (e.g., "membership", "governance") sort_order: i32 # Display order within group } # Option for dropdown or multi_select form fields FormFieldOption = { value: str # Machine value stored in template_data label: str # Human-readable label for UI display description?: str # Optional tooltip/help text } FundTransferDetailResult = { transfer_id: str from_account_id: str to_account_id?: str to_external_address?: str amount: f64 currency: str status: FundTransferStatus created_at: otime } FundTransferListResult = { transfers: [FundTransferDetailResult] } # Treasury operations service types FundTransferResult = { transfer_id: str status: FundTransferStatus } GovernanceError = { code: str message: str } # iDenfy document data extracted from verification IdenfyDocumentData = { doc_first_name?: str doc_last_name?: str doc_dob?: str # Date of birth (YYYY-MM-DD) doc_nationality?: str doc_number?: str # Document number doc_type?: str # PASSPORT, ID_CARD, etc. doc_expiry?: str } IdenfyWebhookData = { client_id: str scan_ref: str status: IdenfyWebhookStatus data?: IdenfyDocumentData } # iDenfy webhook status IdenfyWebhookStatus = { overall: str # Overall verification result auto_document?: str # Automatic document check auto_face?: str # Automatic face check manual_document?: str # Manual document review manual_face?: str # Manual face review } # Verification service types IndividualVerificationResult = { valid: bool resident_id: str status: ResidentStatus verified_at: otime has_full_access: bool name?: str member_since?: otime public_key?: str keypair_version?: i32 verified_by?: str } InviteDetailResult = { invite_id: str company_id: str invitee_email: str proposed_percentage: f64 status: ShareholderInviteStatus expires_at: otime created_at: otime } InviteListResult = { invites: [InviteDetailResult] } # Shareholder service types InviteResult = { invite_id: str token: str status: ShareholderInviteStatus } # Invoice service types InvoiceCreateData = { client_name: str client_email: str items: [InvoiceLineItem] due_date: otime notes?: str } InvoiceError = { code: str message: str } # Invoice line item for creating invoices InvoiceLineItem = { description: str quantity: i32 unit_price: f64 tax_rate?: f64 } InvoiceResult = { invoice: FreeZoneInvoice } InvoiceTotalsResult = { subtotal: f64 tax: f64 total: f64 currency: str } InvoiceUpdateData = { client_name?: str client_email?: str due_date?: otime notes?: str status?: InvoiceStatus } # Invoice service types InvoicesListResult = { invoices: [FreeZoneInvoice] } KeyStatusResult = { has_keypair: bool public_key?: str keypair_version?: i32 } KeypairError = { code: str message: str } # Keypair service types KeypairResult = { public_key: str keypair_version: i32 } KycCompleteResult = { complete: bool verified_at?: otime } KycConfirmData = { identity_verified: bool document_verified: bool liveness_verified: bool verification_level: str } KycConfirmResult = { success: bool resident_activated: bool } KycError = { code: str message: str } # KYC service types KycSessionResult = { kyc_url: str session_id: str expires_at: otime } KycStatusResult = { session_id: str status: KycStatus verified_at?: otime verification_data?: KycVerificationData } # KYC service types KycUserStatusResult = { user_id: str status: KycStatus verified: bool verified_at?: otime } # KYC verification data KycVerificationData = { identity_verified: bool document_verified: bool liveness_verified: bool verification_level: str } # Invoice line item LineItem = { description: str # Item description quantity: i32 # Quantity ordered unit_price: f64 # Price per unit amount: f64 # Total amount (quantity * unit_price) } LinkError = { code: str message: str } LoginResult = { token: str refresh_token: str user_id: str expires_at: otime } LogoutResult = { success: bool } # Message service types MarkAllReadResult = { count: i32 } MarkReadResult = { success: bool marked_count: i32 } MessageError = { code: str message: str } MessageResult = { message: UserMessage } # Message service types MessagesListResult = { messages: [UserMessage] } # Complete onboarding status for a user OnboardingStatus = { tier: i32 # Current tier (0-5) tier_label: str # "Account Created", "Email Verified", etc. steps: [OnboardingStep] # All steps with current status completed_count: i32 # How many steps completed total_count: i32 # Total number of steps wallet_balance: f64 # Current wallet balance next_action?: str # Suggested next step ID } # Individual onboarding step OnboardingStep = { id: str # Step identifier (e.g., "verify_email", "wallet", "kyc") label: str # Display label description: str # What this step does status: OnboardingStepStatus # locked, available, completed action_url?: str # URL to navigate to for this step cost?: f64 # Credit cost (0 for free steps) } PaymentCompleteResult = { complete: bool payment_id?: str } PaymentConfirmResult = { payment_id: str status: PaymentStatus confirmed_at?: otime amount_received?: f64 transaction_hash?: str confirmation_count?: i32 } # Payment service types PaymentCreateData = { amount: str currency: str payment_method: PaymentMethod description?: str } PaymentError = { code: str message: str } PaymentInvoiceResult = { invoice_id: str payment_url: str expires_at: otime } PaymentResult = { payment: Payment } PaymentVerifyResult = { verified: bool status: PaymentStatus } # Payment service types PaymentWalletResult = { wallet_address: str payment_type: PaymentType amount: f64 currency: str crypto_amount: str exchange_rate: str payment_id: str expires_at: otime external_id: str } PaymentsListResult = { payments: [Payment] } PrerequisiteCheckResult = { request_id: str kyc_passed: bool payment_confirmed: bool contracts_signed: bool all_prerequisites_met: bool missing_items: [str] } # Complete pricing breakdown for company registration PricingBreakdown = { company_type: CompanyType subscription_plan: CompanySubscriptionPlan shareholder_count: i32 early_bird: bool setup_fee: FeeBreakdown monthly_fee: FeeBreakdown shareholder_fee?: FeeBreakdown # Only for Global/Cooperative total: f64 # Total amount to pay effective_monthly: f64 # Effective monthly rate } # Pricing error PricingError = { code: str message: str } PricingItem = { item_type: str quantity: i32 unit_price: f64 } PricingPlan = { name: str price: f64 currency: str features: [str] } # Pricing service types PricingPlansResult = { plans: [PricingPlan] } # Pricing calculation result PricingResult = { breakdown: PricingBreakdown } PricingTotalResult = { subtotal: f64 tax: f64 total: f64 currency: str } PrivateKeyResult = { private_key: str } ProposalDetailResult = { proposal_id: str company_id: str title: str description: str category: ProposalCategory status: ProposalStatus proposer_id: str voting_starts_at: otime voting_ends_at: otime quorum_percentage: i32 passing_threshold: i32 votes_for: f64 votes_against: f64 votes_abstain: f64 total_voted: f64 quorum_reached: bool created_at: otime } ProposalListResult = { proposals: [ProposalDetailResult] } # Governance service types ProposalResult = { proposal_id: str status: ProposalStatus } # Public key history types PublicKeyHistoryListResult = { history: [PublicKeyHistory] } # Public key history types PublicKeyHistoryResult = { keys: [PublicKeyHistory] current_version: i32 } RefreshResult = { token: str refresh_token: str expires_at: otime } # Auth service types RegisterResult = { user_id: str email: str verification_sent: bool } RegistrationContractResult = { contract: RegistrationContract } # ─────────────────────────────────────────────────────────────────────────────── # ERROR TYPES # ─────────────────────────────────────────────────────────────────────────────── RegistrationError = { code: str message: str } RegistrationRequestDetailResult = { request_id: str reseller_id: str request_type: RegistrationRequestType client_email: str client_name: str kyc_session_id?: str payment_id?: str signed_contract_ids: [str] status: RegistrationRequestStatus rejection_reason?: str admin_notes?: str submitted_at: otime reviewed_at?: otime reviewed_by?: str } RegistrationRequestError = { code: str message: str } # Admin registration service result types RegistrationRequestFullDetailResult = { request: RegistrationRequestDetailResult kyc_status?: KycStatusResult payment_status?: PaymentStatus payment_amount?: f64 contracts_signed: bool contracts_count: i32 } RegistrationRequestListResult = { requests: [RegistrationRequestDetailResult] } # Registration request service result types RegistrationRequestResult = { request_id: str status: RegistrationRequestStatus submitted_at: otime } # Registration service types RegistrationStatusResult = { user_id: str entity_type: EntityType status: RegistrationFlowStatus contracts_signed: bool payment_complete: bool kyc_complete: bool } RejectInviteResult = { success: bool } RejectRegistrationResult = { request_id: str status: RegistrationRequestStatus reason: str rejected_at: otime } ReportError = { code: str message: str } RequestInfoResult = { request_id: str status: RegistrationRequestStatus message: str } RequiredContractsResult = { contracts: [RegistrationContract] entity_type: EntityType } ResendResult = { sent: bool } ResetPasswordResult = { sent: bool } ResidentCardResult = { resident_id: str name: str status: ResidentStatus member_since: otime public_key?: str keypair_version?: i32 subscription?: SubscriptionInfo # Subscription lifecycle status (computed) } # Resident service types ResidentCreateData = { user_id: str first_name: str last_name: str nationality: str date_of_birth?: otime } ResidentError = { code: str message: str } ResidentPricingResult = { registration_fee: f64 annual_fee: f64 currency: str } ResidentResult = { resident: DigitalResident } ResidentUpdateData = { first_name?: str last_name?: str nationality?: str address?: str } # Resident service types ResidentsListResult = { residents: [DigitalResident] total: i32 page: i32 page_size: i32 } ResolutionDetailResult = { resolution_id: str company_id: str proposal_id: str title: str category: ProposalCategory votes_for: f64 votes_against: f64 quorum_reached: bool passed: bool executed_at?: otime created_at: otime } ResolutionListResult = { resolutions: [ResolutionDetailResult] } ResolutionResult = { resolution_id: str proposal_id: str passed: bool } # Keypair service types RevokeKeypairResult = { revoked: bool } RevokeLinkResult = { revoked: bool } RevokeResult = { success: bool message: str } ShareTransferListResult = { transfers: [ShareTransfer] } ShareTransferResult = { transfer: ShareTransfer } # Shareholder data with ownership percentage ShareholderData = { resident_id: str # Reference to DigitalResident name: str # Full name of shareholder percentage: f64 # Ownership percentage (0-100) } ShareholderError = { code: str message: str } # Shareholder fee rate result ShareholderFeeRateResult = { rate: f64 tier_label: str # e.g., "3-5 shareholders" } # Shareholder fee tier (for tiered pricing) ShareholderFeeTier = { max_count: i32 # Maximum shareholder count for this tier rate: f64 # Rate per shareholder per month } ShareholderInviteListResult = { invites: [ShareholderInvite] } # Shareholder service types (aliases for generator compatibility) ShareholderInviteResult = { invite: ShareholderInvite } ShareholderResult = { shareholder_id: str resident_id: str company_id: str ownership_percentage: f64 } SignContractResult = { signature_id: str contract_id: str signed_at: otime receipt_id: str } SignError = { code: str message: str } SignResult = { signature: str signed_at: otime signer_id: str keypair_version: i32 } SignaturesListResult = { signatures: [ContractSignature] } SignedDocumentsResult = { documents: [ContractSignature] } # Subscription information (computed, returned in auth and resident card) SubscriptionInfo = { plan?: str # Plan code: 1_month, 6_months, 1_year, 2_years, 3_years status: SubscriptionStatus # Computed from subscription_end_date vs now start_date?: otime # When current subscription period started end_date?: otime # When current subscription period ends days_remaining?: i32 # Days until expiry (negative if expired) auto_renewal: bool # Whether auto-renewal is enabled } SubscriptionResult = { success: bool plan: str auto_renewal: bool subscription_end_date?: otime } TallyResult = { proposal_id: str votes_for: f64 votes_against: f64 votes_abstain: f64 total_voted: f64 quorum_reached: bool passed: bool status: ProposalStatus } # Tax service types TaxCalculationResult = { gross_revenue: f64 deductions: f64 taxable_income: f64 tax_rate: f64 tax_due: f64 currency: str } TaxDeadlineItem = { filing_type: TaxFilingPeriod period_start: otime period_end: otime due_date: otime status: str } TaxDeadlineResult = { deadlines: [TaxDeadlineItem] } TaxDocumentDetailResult = { document_id: str filing_id: str document_type: str name: str generated: bool created_at: otime } TaxDocumentListResult = { documents: [TaxDocumentDetailResult] } TaxDocumentResult = { document_id: str name: str } TaxError = { code: str message: str } TaxFilingDetailResult = { filing_id: str company_id: str filing_type: TaxFilingPeriod period_start: otime period_end: otime due_date: otime status: TaxFilingStatus gross_revenue: f64 cost_of_goods_sold: f64 gross_profit: f64 operating_expenses: f64 net_income: f64 tax_rate: f64 tax_due: f64 created_at: otime } TaxFilingListResult = { filings: [TaxFilingDetailResult] } # Tax service types TaxFilingResult = { filing_id: str status: TaxFilingStatus } TemplateResult = { template: ContractTemplate } TemplatesListResult = { templates: [ContractTemplate] } TransactionResult = { transaction: TreasuryTransaction } TransactionsListResult = { transactions: [TreasuryTransaction] } TransferDetailResult = { transfer_id: str company_id: str from_resident_id: str to_resident_id: str percentage: f64 status: ShareTransferStatus requires_vote: bool created_at: otime } TransferListResult = { transfers: [TransferDetailResult] } TransferResult = { transfer_id: str status: ShareTransferStatus } TreasuryAccountResult = { account: TreasuryAccount } # Treasury service types TreasuryAccountsListResult = { accounts: [TreasuryAccount] } # Treasury service types TreasuryAccountsResult = { accounts: [TreasuryAccount] } TreasuryError = { code: str message: str } TreasuryOperationsError = { code: str message: str } TreasuryTransactionsResult = { transactions: [TreasuryTransaction] } UnreadCountResult = { count: i32 } # Document service types UnsignedDocumentsResult = { documents: [RegistrationContract] } UpdateCompanyData = { name?: str description?: str address?: Address shareholders?: [ShareholderData] email?: str phone?: str website?: str industry?: str status?: CompanyStatus } UpdateContractData = { name?: str counterparty?: str status?: ContractStatus end_date?: otime total_value?: f64 } UpdateExpenseData = { receipt_number?: str vendor_name?: str description?: str amount?: f64 currency?: str category?: str receipt_url?: str expense_date?: otime status?: ExpenseStatus } UpdateInvoiceData = { amount?: f64 tax_amount?: f64 description?: str line_items?: [LineItem] due_date?: otime status?: InvoiceStatus } UpdateResidentData = { first_name?: str last_name?: str phone?: str status?: ResidentStatus } UpdateTreasuryAccountData = { name?: str bank_name?: str iban?: str account_number?: str bic?: str } UserInfo = { id: str email: str first_name?: str last_name?: str is_active: bool email_verified: bool kyc_verified: bool kyc_verified_at?: otime subscription?: SubscriptionInfo # Subscription lifecycle status (computed) } ValidationResult = { valid: bool reseller_id?: str permissions: [str] } VerificationError = { code: str message: str } # Verification service types VerificationLinkResult = { link: VerificationLink } VerificationLinksListResult = { links: [VerificationLink] } # Verification service additional types VerificationLinksResult = { links: [VerificationLink] } VerifyAllSignedResult = { all_signed: bool unsigned_count: i32 } VerifyByTokenResult = { valid: bool entity_type: EntityType entity_id: str status: str verified_at: otime has_full_access: bool name?: str public_key?: str keypair_version?: i32 member_since?: otime } VerifyEmailResult = { success: bool message: str } VerifyError = { code: str message: str } VerifyLinkResult = { valid: bool user_id?: str company_id?: str permissions: [str] } VerifyResult = { valid: bool signer_id: str signature_keypair_version: i32 current_keypair_version: i32 is_historical_key: bool document_type?: DocumentType document_hash?: str signed_at?: otime error?: str } # Typed visibility decision sub-record attached to UserAuditLog rows whose action is # "view_document". Session 50 adds this to record WHY can_view returned the answer it # did — which audience row was consulted, which of the subject's CompanyRole rows # matched, which classification tiers intersected, what as_of_epoch was used, whether # the audience row was materialized-on-first-read (and by which rule version). # Redundancy between `projection_applied: bool` and `audience_computation: str` is # intentional: the bool is the fast filter ("was this a projected row?"); the string # is the lookup key for session-51+ reprojection sweeps ("which rule version?"). VisibilityDecision = { audience_sid: str # sid of the DocumentAudience row consulted matched_role_sids: [str] # sids of the subject's CompanyRole rows that # satisfied BOTH window-overlap AND classification- # intersect; empty when can_view=false classification_intersected: [str] # the classification tiers that actually # intersected (subset of both the doc's and the # role's classifications); empty when can_view=false as_of_epoch: otime # when can_view was called (the "now" the # window-overlap was computed against) projection_applied: bool # true iff the audience_sid row was written by # resolve_or_project on first read (not hand-authored) audience_computation: str # copy of the consulted row's audience_computation } # Governance service types VoteListResult = { votes: [Vote] } VoteResult = { vote_id: str proposal_id: str vote_value: VoteValue voting_power: f64 } VotingPowerResult = { resident_id: str company_id: str voting_power: f64 voting_model: VotingModel shareholding_percentage: f64 } WebhookError = { code: str message: str } WebhookResult = { success: bool message: str } # ─────────────────────────────────────────────────────────────────────────────── # RPC SERVICES - Custom business logic methods # ─────────────────────────────────────────────────────────────────────────────── service AdminRegistrationService { version: "1.0.0" description: "Admin-side registration request approval workflow" # List pending registrations with optional filtering list_pending_registrations(status?: RegistrationRequestStatus, reseller_id?: str) -> RegistrationRequestListResult error: AdminRegistrationError # Get full registration details with linked KYC, payment, contracts get_registration_details(request_id: str) -> RegistrationRequestFullDetailResult error: AdminRegistrationError # Automated check that prerequisites are met verify_registration_prerequisites(request_id: str) -> PrerequisiteCheckResult error: AdminRegistrationError # Approve registration and create official record approve_registration(request_id: str, admin_email: str, admin_signature: str) -> ApproveRegistrationResult error: AdminRegistrationError # Reject registration with explanation reject_registration(request_id: str, admin_email: str, reason: str) -> RejectRegistrationResult error: AdminRegistrationError # Request additional info from reseller request_additional_info(request_id: str, admin_email: str, message: str) -> RequestInfoResult error: AdminRegistrationError } service ApiKeyService { version: "1.0.0" description: "API key management for resellers" # Generate API key generate(client_id: str, client_secret: str, access_password: str) -> ApiKeyResult error: ApiKeyError } # ─────────────────────────────────────────────────────────────────────────────── # RPC SERVICES - Custom business logic (beyond CRUD) # ─────────────────────────────────────────────────────────────────────────────── service AuthService { version: "1.0.0" description: "User authentication and session management" # Register a new user register(email: str, password: str, first_name?: str, last_name?: str) -> AuthResult error: RegistrationError # Login user and return JWT token login(email: str, password: str) -> AuthResult error: AuthError # Refresh JWT token refresh_token(token: str) -> AuthResult error: AuthError # Fetch current user state (side-effect-free — no token rotation). # Frontends call this after mutations that change user state (KYC, email # verification, subscription renewal) to refresh their local cache. # Backend is SSOT; frontend caches are convenience, not authority. me(user_id: str) -> UserInfo error: AuthError # Send email verification send_verification_email(email: str) -> VerifyEmailResult error: EmailError # Verify email with token verify_email(token: str) -> VerifyEmailResult error: EmailError # Change password change_password(user_id: str, old_password: str, new_password: str) -> ChangePasswordResult error: AuthError } service BusinessDocumentService { version: "1.0.0" description: "Business document management" # Upload document upload_document(company_id: str, document_type: BusinessDocumentType, name: str, description?: str, file_url: str, uploaded_by: str) -> BusinessDocumentResult error: BusinessDocumentError # Generate official document generate_official_document(company_id: str, document_type: BusinessDocumentType) -> BusinessDocumentResult error: BusinessDocumentError # Get document get_document(document_id: str) -> BusinessDocumentDetailResult error: BusinessDocumentError # List documents for a company list_documents(company_id: str, document_type?: BusinessDocumentType) -> BusinessDocumentListResult error: BusinessDocumentError # Delete document (non-official only) delete_document(document_id: str) -> DeleteBusinessDocumentResult error: BusinessDocumentError } service CompanyRoleService { version: "1.0.0" description: "CompanyRole lookups (role + time + classification, FREEZONE.md principle 2)" # List roles held at a company at a given instant. # as_of_epoch == 0 means "now". Filters by valid_from <= as_of AND (valid_to IS NULL OR valid_to > as_of). list_by_company(company_id: str, as_of_epoch: i64) -> CompanyRoleListResult error: CompanyRoleError # List roles a subject (resident or corporate shareholder) holds across all companies at a given instant. list_by_resident(subject_id: str, as_of_epoch: i64) -> CompanyRoleListResult error: CompanyRoleError } service CompanyService { version: "1.0.0" description: "Free zone company management" # List companies for user with pagination list(user_id: str, page: i32, page_size: i32) -> CompaniesListResult error: CompanyError # Create a new company create(user_id: str, data: CreateCompanyData) -> CompanyResult error: CompanyError # Get company by ID get(company_id: str) -> CompanyResult error: CompanyError # Update company update(company_id: str, data: UpdateCompanyData) -> CompanyResult error: CompanyError # Delete company delete(company_id: str) -> DeleteResult error: CompanyError # Get company card (public display info) get_card(company_id: str) -> CompanyCardResult error: CompanyError # Get company key status get_key_status(company_id: str) -> KeyStatusResult error: KeypairError # Get company encrypted private key blob (client decrypts locally) get_company_encrypted_key(company_id: str) -> EncryptedKeyResult error: KeypairError # Store client-generated company keypair (replaces existing) store_company_keypair(company_id: str, public_key: str, encrypted_private_key: str) -> KeypairResult error: KeypairError } service ContractService { version: "1.0.0" description: "Business contracts and templates" # List company contracts list_company_contracts(fzc_id: str) -> ContractsListResult error: ContractError # Get company contract get_company_contract(fzc_id: str, contract_id: str) -> ContractResult error: ContractError # Create company contract create_company_contract(fzc_id: str, data: CreateContractData) -> ContractResult error: ContractError # Update company contract update_company_contract(fzc_id: str, contract_id: str, data: UpdateContractData) -> ContractResult error: ContractError # Archive company contract archive_company_contract(fzc_id: str, contract_id: str) -> ContractResult error: ContractError # List contract templates list_templates() -> TemplatesListResult error: ContractError # Get contract template get_template(template_id: str) -> TemplateResult error: ContractError # Get required registration contracts get_required_contracts(entity_type: EntityType) -> RequiredContractsResult error: ContractError # List every registration contract template known to the freezone — # formation contracts (M&A, Company Declaration, Bylaws) and lifecycle # templates (ma_amendment, share_transfer, buyback, expulsion, upgrade # resolution, etc.). Used by the Documents page to resolve any # ContractSignature's contract_id to a human-readable name + category, # including signatures from events that `get_required_contracts` filters # out because they are not required at creation time. list_all_contracts() -> RequiredContractsResult error: ContractError # Sign contract (no password — session auth is sufficient) sign_contract(user_id: str, contract_id: str, entity_type: str, entity_name: str, entity_id: str) -> SignContractResult error: SignError # Get contract signatures for user get_signatures(user_id: str) -> SignaturesListResult error: ContractError } service CreditService { version: "1.0.0" description: "Credit balance management — top-up, deduction, and transaction history" # Get user's credit balance (creates if not exists) get_balance(user_id: str) -> CreditBalanceResult error: CreditError # Top up credits from a confirmed payment top_up(user_id: str, amount: f64, payment_id: str) -> CreditTransactionResult error: CreditError # Deduct credits for a service (KYC, subscription, company registration) deduct(user_id: str, amount: f64, description: str, reference_type: str, reference_id: str) -> CreditTransactionResult error: CreditError # Add bonus credits (welcome bonus, promotions) add_bonus(user_id: str, amount: f64, description: str) -> CreditTransactionResult error: CreditError # Refund credits (e.g., failed KYC) refund(user_id: str, amount: f64, description: str, reference_type: str, reference_id: str) -> CreditTransactionResult error: CreditError # List credit transactions for a user list_transactions(user_id: str, page: i32, page_size: i32) -> CreditTransactionsListResult error: CreditError # Get credit pricing (costs for services) get_pricing() -> CreditPricingResult error: CreditError # Get onboarding status (progressive unlock tiers) get_onboarding_status(user_id: str) -> OnboardingStatus error: CreditError # Initiate a wallet top-up via a specific payment provider initiate_topup(user_id: str, amount: f64, provider: str) -> ClickPesaResult error: CreditError # Set up auto top-up via Stripe (saves card, sets threshold/amount) setup_auto_topup(user_id: str, amount: f64, threshold: f64) -> AutoTopupResult error: CreditError # Disable auto top-up disable_auto_topup(user_id: str) -> AutoTopupResult error: CreditError # Activate resident after credit deduction (keypair generated client-side) activate_resident(user_id: str, plan: str, public_key: str, encrypted_private_key: str) -> ActivateResult error: CreditError } service DocumentAudienceService { version: "1.0.0" description: "Document visibility (FREEZONE.md principle 2: who can read document D right now, computed from CompanyRole × classification)" # Answer "may subject_id read document_ref right now?". # Composes (CompanyRole rows where window overlaps document's relevant-period) # × (classification intersection). Returns false if no DocumentAudience row # exists for document_ref (legacy fallback policy deferred to first consumer; # see decisions/004-document-audience.md). can_view(subject_id: str, document_ref: str) -> DocumentCanViewResult error: DocumentAudienceError # List all DocumentAudience rows for a document_ref, newest first. # Used by audit trail and supersession-chain inspection. list_by_document(document_ref: str) -> DocumentAudienceListResult error: DocumentAudienceError } service DocumentService { version: "2.0.0" description: "Client-side document signing — signature submitted after browser-side Ed25519 sign" # Submit a client-signed document (signature created in WASM, server verifies with stored public key) submit_signed(entity_type: EntityType, entity_id: str, document_type: DocumentType, document_hash: str, signature: str) -> SignResult error: SignError # Verify document signature verify(signature: str, document_hash: str, signer_id: str, signer_type: EntityType) -> VerifyResult error: VerifyError # Get key status get_key_status(user_id: str) -> KeyStatusResult error: KeypairError } service EntityDraftService { version: "1.0.0" description: "Multi-stakeholder entity creation with invite/countersign flow" # Get dynamic form schema for a company/coop type get_form_schema(entity_type: str, company_type: CompanyType) -> FormSchemaResult error: DraftError # Creator: create a draft entity create_draft(user_id: str, entity_type: str, entity_name: str, company_type: CompanyType, template_data: str, shareholders: [ShareholderData]) -> DraftResult error: DraftError # Creator: preview rendered documents before finalizing preview_documents(draft_id: str) -> PreviewDocumentsResult error: DraftError # Creator: finalize draft and send invitations finalize_draft(draft_id: str) -> DraftResult error: DraftError # Creator: cancel draft cancel_draft(draft_id: str) -> DraftResult error: DraftError # Creator: get draft status with signature progress get_draft_status(draft_id: str) -> DraftStatusResult error: DraftError # Creator: list drafts for user list_drafts(user_id: str) -> DraftsListResult error: DraftError # Invitee: get my pending invitations get_my_invitations(user_id: str) -> InvitationsListResult error: DraftError # Invitee: get invitation documents for review get_invitation_documents(invitation_id: str) -> InvitationDocumentsResult error: DraftError # Invitee: accept invitation and sign accept_invitation(invitation_id: str, user_id: str) -> InvitationResult error: DraftError # Invitee: decline invitation decline_invitation(invitation_id: str, user_id: str, reason?: str) -> InvitationResult error: DraftError # Check if all signatures collected, trigger payment + activation check_completion(draft_id: str) -> CompletionResult error: DraftError # Creator: pay for entity after all signatures collected pay_and_activate(draft_id: str, user_id: str, subscription_plan: CompanySubscriptionPlan) -> DraftResult error: DraftError } service ExpenseService { version: "1.0.0" description: "Expense management for companies" # List expenses for company list(fzc_id: str) -> ExpensesListResult error: InvoiceError # Create expense create(fzc_id: str, data: CreateExpenseData) -> ExpenseResult error: InvoiceError # Get expense by ID get(expense_id: str) -> ExpenseResult error: InvoiceError # Update expense update(expense_id: str, data: UpdateExpenseData) -> ExpenseResult error: InvoiceError # Delete expense delete(fzc_id: str, expense_id: str) -> DeleteResult error: InvoiceError # Submit expense for approval submit(expense_id: str) -> ExpenseResult error: InvoiceError # Approve expense approve(expense_id: str, approved_by: str) -> ExpenseResult error: InvoiceError # Reject expense reject(expense_id: str, rejected_by: str, reason: str) -> ExpenseResult error: InvoiceError } service GovernanceService { version: "1.0.0" description: "Proposal creation, voting, and resolution management" # Create a new proposal. `kind` drives the threshold snapshot (Ordinary/Special/Board); # quorum + passing percentages are read from branding.toml [governance.resolution_thresholds] # at creation time and stamped onto the Proposal row immutably (session 56 / D-014). create_proposal(company_id: str, title: str, description: str, category: ProposalCategory, kind: ResolutionKind, voting_days: i32, allow_abstention: bool) -> ProposalResult error: GovernanceError # Get proposal details get_proposal(proposal_id: str) -> ProposalDetailResult error: GovernanceError # List proposals for a company list_proposals(company_id: str, status?: ProposalStatus) -> ProposalListResult error: GovernanceError # Cast a vote on a proposal cast_vote(proposal_id: str, voter_id: str, vote_value: VoteValue, comment?: str, password: str) -> VoteResult error: GovernanceError # Get voting power for a shareholder get_voting_power(company_id: str, resident_id: str) -> VotingPowerResult error: GovernanceError # Tally votes and update proposal status tally_votes(proposal_id: str) -> TallyResult error: GovernanceError # Execute a passed proposal (create resolution) execute_proposal(proposal_id: str, executor_id: str, notes?: str) -> ResolutionResult error: GovernanceError # List resolutions for a company list_resolutions(company_id: str) -> ResolutionListResult error: GovernanceError } service InvoiceService { version: "1.0.0" description: "Invoice management for companies" # List invoices for company list(fzc_id: str) -> InvoicesListResult error: InvoiceError # Create invoice create(fzc_id: str, data: CreateInvoiceData) -> InvoiceResult error: InvoiceError # Get invoice by ID get(invoice_id: str) -> InvoiceResult error: InvoiceError # Update invoice update(invoice_id: str, data: UpdateInvoiceData) -> InvoiceResult error: InvoiceError # Delete invoice delete(fzc_id: str, invoice_id: str) -> DeleteResult error: InvoiceError } service KeypairService { version: "2.0.0" description: "Client-side keypair storage and verification — private key never leaves the browser" # Store client-generated keypair (public key + encrypted private key blob) store_client_keypair(entity_type: EntityType, entity_id: str, public_key: str, encrypted_private_key: str) -> KeypairResult error: KeypairError # Get encrypted private key blob (client decrypts locally) get_encrypted_private_key(entity_type: EntityType, entity_id: str) -> EncryptedKeyResult error: KeypairError # Verify signature using stored public key verify_signature(signature: str, document_hash: str, signer_id: str, signer_type: EntityType) -> VerifyResult error: VerifyError } service KycService { version: "1.0.0" description: "KYC verification management" # Start KYC verification session start_verification(resident_id: str, public_key: str, callback_url?: str) -> KycSessionResult error: KycError # Check KYC status check_status(session_id: str) -> KycStatusResult error: KycError # Handle iDenfy webhook handle_idenfy_webhook(data: IdenfyWebhookData) -> WebhookResult error: WebhookError } service MessageService { version: "1.0.0" description: "User and company messaging" # List user messages with filtering list_user_messages(user_id: str, category?: MessageCategory, is_read?: bool) -> MessagesListResult error: MessageError # Get user message get_user_message(user_id: str, message_id: str) -> MessageResult error: MessageError # Get unread count get_unread_count(user_id: str) -> UnreadCountResult error: MessageError # Mark message as read mark_as_read(user_id: str, message_id: str) -> MarkReadResult error: MessageError # Mark message as unread mark_as_unread(user_id: str, message_id: str) -> MarkReadResult error: MessageError # Mark all as read mark_all_read(user_id: str) -> MarkReadResult error: MessageError # Mark all as unread mark_all_unread(user_id: str) -> MarkReadResult error: MessageError # Delete message delete_message(user_id: str, message_id: str) -> DeleteResult error: MessageError # Create system message (admin) create_system_message(user_id: str, title: str, body: str, category?: MessageCategory, action_url?: str) -> MessageResult error: MessageError # List company messages list_company_messages(company_id: str) -> MessagesListResult error: MessageError # Create company message create_company_message(company_id: str, title: str, body: str, category?: MessageCategory) -> MessageResult error: MessageError # Mark company message as read mark_company_read(company_id: str, message_id: str) -> MarkReadResult error: MessageError # Mark company message as unread mark_company_unread(company_id: str, message_id: str) -> MarkReadResult error: MessageError # Mark all company messages read mark_all_company_read(company_id: str) -> MarkReadResult error: MessageError # Mark all company messages unread mark_all_company_unread(company_id: str) -> MarkReadResult error: MessageError } service PaymentService { version: "1.0.0" description: "Payment processing for residents and companies" # Get crypto wallet address for payment get_payment_wallet(resident_id: str, payment_type: PaymentType, amount: f64, currency: str) -> PaymentWalletResult error: PaymentError # Confirm payment confirm_payment(payment_id: str, sender_address?: str, transaction_hash?: str) -> PaymentConfirmResult error: PaymentError # Initiate ClickPesa payment initiate_clickpesa(entity_id: str, entity_type: EntityType, amount: f64, currency: str, description: str, payment_purpose: str, subscription_plan?: str) -> ClickPesaResult error: PaymentError # Handle ClickPesa webhook handle_payment_webhook(data: ClickPesaWebhookData) -> WebhookResult error: WebhookError # Renew a resident's subscription. Debits the caller's credit wallet for # the plan amount (with duration discount) and extends subscription_end_date # from max(current_end, now). Plan is one of: 1_month, 6_months, 1_year, # 2_years, 3_years. Wallet-only — use the ClickPesa/Stripe top-up flows to # add credits before renewing. initiate_renewal(user_id: str, plan: str) -> SubscriptionInfo error: PaymentError # Renew a company's subscription. Debits the caller's credit wallet for # monthly_fee × months(plan) × (1 - company_discount(plan)), extends the # company's subscription_end_date, and posts an inbox message. No setup fee. # Plan is a branding.toml plan code (e.g. 1_month, 6_months, 1_year, # 2_years, 3_years) — same vocabulary as resident renewal. initiate_company_renewal(user_id: str, company_id: str, plan: str) -> SubscriptionInfo error: PaymentError # Get subscription status for a user get_subscription_status(user_id: str) -> SubscriptionInfo error: PaymentError } service PricingService { version: "1.0.0" description: "Pricing calculation for residents and companies" # Calculate company registration pricing calculate_company_pricing(company_type: CompanyType, shareholder_count: i32, subscription_plan: CompanySubscriptionPlan, early_bird: bool) -> PricingResult error: PricingError # Get all pricing configurations (for display) get_all_pricing() -> AllPricingConfig error: PricingError # Get shareholder fee rate for a company type and count get_shareholder_fee_rate(company_type: CompanyType, shareholder_count: i32) -> ShareholderFeeRateResult error: PricingError } service PublicKeyHistoryService { version: "1.0.0" description: "Historical public key management for signature verification" # Get key history for an entity get_history(entity_type: EntityType, entity_id: str) -> PublicKeyHistoryResult error: KeypairError # Get specific historical key by version get_by_version(entity_type: EntityType, entity_id: str, version: i32) -> PublicKeyHistory error: KeypairError # Verify signature with historical key lookup verify_with_history(signature: str, entity_type: EntityType, entity_id: str) -> VerifyResult error: VerifyError } service RegistrationRequestService { version: "1.0.0" description: "Reseller-side registration request management" # Submit a new registration request submit_registration_request(reseller_id: str, request_type: RegistrationRequestType, client_email: str, client_name: str, kyc_session_id?: str, payment_id?: str, signed_contract_ids: [str], reseller_signature: str) -> RegistrationRequestResult error: RegistrationRequestError # Check status of a registration request get_registration_request_status(request_id: str) -> RegistrationRequestDetailResult error: RegistrationRequestError # List all requests for a reseller list_registration_requests(reseller_id: str) -> RegistrationRequestListResult error: RegistrationRequestError # Update a registration request (before approval, e.g., after needs_info) update_registration_request(request_id: str, client_email?: str, client_name?: str, kyc_session_id?: str, payment_id?: str, signed_contract_ids?: [str]) -> RegistrationRequestDetailResult error: RegistrationRequestError } service RegistrationService { version: "1.0.0" description: "Registration flow management" # Get registration status get_status(user_id: str, entity_type: EntityType) -> RegistrationStatusResult error: RegistrationError # Update registration status update_status(user_id: str, entity_type: EntityType, status: RegistrationFlowStatus) -> RegistrationStatusResult error: RegistrationError # Check if resident payment is complete check_payment_complete(user_id: str) -> PaymentCompleteResult error: RegistrationError # Check if user KYC is complete check_kyc_complete(user_id: str) -> KycCompleteResult error: RegistrationError # Activate resident if all requirements met activate_if_complete(user_id: str) -> ActivateResult error: RegistrationError # Confirm KYC (internal) confirm_kyc(user_id: str, resident_id: str, data: KycConfirmData) -> KycConfirmResult error: KycError } service ReportService { version: "1.0.0" description: "Financial report generation" # Generate balance sheet generate_balance_sheet(company_id: str, as_of_date: otime, generated_by: str) -> FinancialReportResult error: ReportError # Generate income statement generate_income_statement(company_id: str, period_start: otime, period_end: otime, generated_by: str) -> FinancialReportResult error: ReportError # Generate cash flow statement generate_cash_flow(company_id: str, period_start: otime, period_end: otime, generated_by: str) -> FinancialReportResult error: ReportError # Generate trial balance generate_trial_balance(company_id: str, as_of_date: otime, generated_by: str) -> FinancialReportResult error: ReportError # Get report get_report(report_id: str) -> FinancialReportDetailResult error: ReportError # List reports for a company list_reports(company_id: str, report_type?: FinancialReportType) -> FinancialReportListResult error: ReportError } service ResidentService { version: "1.0.0" description: "Digital resident management" # List residents with pagination list(user_id: str, page: i32, page_size: i32) -> ResidentsListResult error: ResidentError # Create a new resident create(user_id: str, data: CreateResidentData) -> ResidentResult error: ResidentError # Get resident by ID get(resident_id: str) -> ResidentResult error: ResidentError # Update resident update(resident_id: str, data: UpdateResidentData) -> ResidentResult error: ResidentError # Delete resident delete(resident_id: str) -> DeleteResult error: ResidentError # Update subscription update_subscription(resident_id: str, plan: str, auto_renewal: bool) -> SubscriptionResult error: ResidentError # Get resident card (public display info) get_card(user_id: str) -> ResidentCardResult error: ResidentError } service ShareholderService { version: "1.0.0" description: "Shareholder management, invites, and share transfers" # Invite someone to become a shareholder invite_shareholder(company_id: str, inviter_id: str, invitee_email: str, proposed_percentage: f64, message?: str) -> InviteResult error: ShareholderError # Accept shareholder invite accept_invite(invite_token: str, resident_id: str) -> AcceptInviteResult error: ShareholderError # Reject shareholder invite reject_invite(invite_token: str) -> RejectInviteResult error: ShareholderError # List pending invites for a company list_invites(company_id: str) -> InviteListResult error: ShareholderError # Initiate share transfer initiate_transfer(company_id: str, from_resident_id: str, to_resident_id: str, percentage: f64, reason?: str) -> TransferResult error: ShareholderError # Approve share transfer (by recipient) approve_transfer(transfer_id: str, to_signature: str) -> TransferResult error: ShareholderError # Complete share transfer complete_transfer(transfer_id: str) -> TransferResult error: ShareholderError # Cancel share transfer cancel_transfer(transfer_id: str) -> TransferResult error: ShareholderError # List share transfers for a company list_transfers(company_id: str, status?: ShareTransferStatus) -> TransferListResult error: ShareholderError # Initiate dividend distribution initiate_dividend(company_id: str, total_amount: f64, currency: str, source_account_id: str, distribution_date: otime, initiated_by: str) -> DividendResult error: ShareholderError # Execute dividend distribution execute_dividend(dividend_id: str) -> DividendResult error: ShareholderError # List dividend distributions for a company list_dividends(company_id: str) -> DividendListResult error: ShareholderError } service TaxService { version: "1.0.0" description: "Tax filing and compliance management" # Create tax filing create_filing(company_id: str, filing_type: TaxFilingPeriod, period_start: otime, period_end: otime) -> TaxFilingResult error: TaxError # Get tax filing get_filing(filing_id: str) -> TaxFilingDetailResult error: TaxError # Update tax filing (draft only) update_filing(filing_id: str, gross_revenue: f64, cost_of_goods_sold: f64, operating_expenses: f64) -> TaxFilingResult error: TaxError # Submit tax filing submit_filing(filing_id: str, submitted_by: str, password: str) -> TaxFilingResult error: TaxError # List tax filings for a company list_filings(company_id: str, status?: TaxFilingStatus) -> TaxFilingListResult error: TaxError # Get upcoming filing deadlines get_deadlines(company_id: str) -> TaxDeadlineResult error: TaxError # Generate tax document generate_document(filing_id: str, document_type: str) -> TaxDocumentResult error: TaxError # List tax documents for a filing list_documents(filing_id: str) -> TaxDocumentListResult error: TaxError } service TreasuryOperationsService { version: "1.0.0" description: "Fund transfers and currency exchange" # Initiate internal fund transfer initiate_transfer(from_account_id: str, to_account_id: str, amount: f64, currency: str, reference?: str, initiated_by: str) -> FundTransferResult error: TreasuryOperationsError # Initiate external fund transfer initiate_external_transfer(from_account_id: str, to_address: str, to_name: str, amount: f64, currency: str, reference?: str, initiated_by: str) -> FundTransferResult error: TreasuryOperationsError # Approve fund transfer approve_transfer(transfer_id: str, approved_by: str) -> FundTransferResult error: TreasuryOperationsError # Cancel fund transfer cancel_transfer(transfer_id: str) -> FundTransferResult error: TreasuryOperationsError # List fund transfers for an account list_transfers(account_id: str, status?: FundTransferStatus) -> FundTransferListResult error: TreasuryOperationsError # Get exchange rate quote get_exchange_quote(from_currency: str, to_currency: str, amount: f64) -> ExchangeQuoteResult error: TreasuryOperationsError # Execute currency exchange execute_exchange(owner_type: EntityType, owner_id: str, from_account_id: str, to_account_id: str, from_amount: f64, from_currency: str, exchange_rate: f64, initiated_by: str) -> ExchangeResult error: TreasuryOperationsError # List exchange transactions list_exchanges(owner_type: EntityType, owner_id: str) -> ExchangeListResult error: TreasuryOperationsError } service TreasuryService { version: "1.0.0" description: "Treasury account and transaction management" # List company treasury accounts list_company_accounts(company_id: str) -> TreasuryAccountsResult error: TreasuryError # Get company treasury account get_company_account(company_id: str, account_id: str) -> TreasuryAccountResult error: TreasuryError # List company treasury transactions list_company_transactions(company_id: str, account_id: str) -> TreasuryTransactionsResult error: TreasuryError # Create company treasury account create_company_account(company_id: str, data: CreateTreasuryAccountData) -> TreasuryAccountResult error: TreasuryError # Update company treasury account update_company_account(company_id: str, account_id: str, data: UpdateTreasuryAccountData) -> TreasuryAccountResult error: TreasuryError # List resident treasury accounts list_resident_accounts(resident_id: str) -> TreasuryAccountsResult error: TreasuryError # Get resident treasury account get_resident_account(resident_id: str, account_id: str) -> TreasuryAccountResult error: TreasuryError # List resident treasury transactions list_resident_transactions(resident_id: str, account_id: str) -> TreasuryTransactionsResult error: TreasuryError # Create resident treasury account create_resident_account(resident_id: str, data: CreateTreasuryAccountData) -> TreasuryAccountResult error: TreasuryError # Update resident treasury account update_resident_account(resident_id: str, account_id: str, data: UpdateTreasuryAccountData) -> TreasuryAccountResult error: TreasuryError } service UserAuditLogService { version: "1.0.0" description: "Typed append-only audit trail (FREEZONE.md principle 4). Session 49 seed." # Append a new audit row. No update, no delete — audit is append-only by construction. # Callers set allowed=true on success, allowed=false on denial/validation-fail. # `context` is caller-serialized JSON (correlation_id, request origin, redacted reason…). log(subject_id: str, action: AuditAction, target_ref: str, target_type: AuditTargetType, allowed: bool, context: str) -> UserAuditLogResult error: UserAuditLogError # List rows for a subject ("what did this actor do?"). Newest first, capped at `limit`. # limit <= 0 means server default (100). list_for_subject(subject_id: str, limit: i32) -> UserAuditLogListResult error: UserAuditLogError # List rows for a target ("who touched this entity?"). Newest first, capped at `limit`. # limit <= 0 means server default (100). list_for_target(target_ref: str, limit: i32) -> UserAuditLogListResult error: UserAuditLogError } service VerificationService { version: "1.0.0" description: "Tiered verification for individuals and companies" # Verify individual (Tier 1: anonymous, Tier 2: member, Tier 3: via link) verify_individual(resident_id: str, requester_tier: i32, link_token?: str) -> IndividualVerificationResult error: VerificationError # Verify company (Tier 1: anonymous, Tier 2: member, Tier 3: via link) verify_company(company_id: str, requester_tier: i32, link_token?: str) -> CompanyVerificationResult error: VerificationError # Create verification link create_verification_link(entity_type: EntityType, entity_id: str, label?: str, expires_in_hours: i32, single_use: bool) -> CreateLinkResult error: LinkError # Revoke verification link revoke_verification_link(link_id: str) -> RevokeResult error: LinkError # List verification links for user list_verification_links(user_id: str) -> VerificationLinksResult error: LinkError # Delete inactive verification links delete_inactive_links(user_id: str) -> DeleteLinksResult error: LinkError # Verify by token (public endpoint) verify_by_token(token: str) -> VerifyByTokenResult error: VerificationError } # ─────────────────────────────────────────────────────────────────────────────── # ENUMS - String literal union types # ─────────────────────────────────────────────────────────────────────────────── # AI provider for user AI agent AiProvider = "groq" | "openrouter" | "sambanova" | "deepinfra" # AuditAction — kinds of audited actions. Session 49 seeds 4 action variants (the mutation # sites wired this session) plus an "other" fallback for generic UserAuditLogService.log # callers. Session 50 adds view_document for can_view-consumer audit rows (paired with # a typed VisibilityDecision sub-record). Session 53 adds appoint_director + remove_director # for Director CompanyRole lifecycle (M&A Art. 17/18). Extend when new audited mutation # sites are wired. # Deliberately distinct from DocumentKind: DocumentKind discriminates document *entities*; # AuditAction discriminates *actions* taken against any kind of target. # # Session 54 adds "grant_signing_authority" / "revoke_signing_authority" for delegated # SigningAuthority lifecycle (Board Resolution-granted signing per M&A Art. 3 + Alex Q1). # Director-auto signing is NOT audited here — the underlying Director CompanyRole # appointment already produces an "appoint_director" audit row (session 53), and there is # no separate stored row to grant/revoke. See decisions/012-signing-authority.md (virtual # director-auto scope — B.5 catch). # # Session 55 adds "governance_step" (single variant covering all 5 due-process steps; # the specific step is stored on GovernanceAction.kind, not duplicated on the audit # enum — keeps the enum tight, matches D-013 audit-shape option a) and "enforce_decision" # (separate variant because enforcement is the closure side-effect, not a state-chain # step — pairing it with the closure-bearing handler gives per-step filter granularity # exactly where it's load-bearing). # # Session 57 adds "revoke_api_key" + "rotate_api_key" for personal ApiKey lifecycle # (list/revoke/rotate handlers per #311). Generation is not audited here because # `apikeyservice.generate` predates the audit primitive and exists on an unrelated # authorization path (password re-confirm at generate time); revoke + rotate are the # post-creation mutations worth observing. See decisions/015-personal-api-key-mgmt.md. # Session 59 adds "ai_query_debit" for the idempotent post-debit write on aibroker # billing rows. See decisions/017-ai-billing-freezone-side.md. # Session 63 adds "kyc_artifact_submit" for the partner-track π-4 delegated-KYC # attestation write. Fires once per KycArtifactAttestation row creation from the # partner-aware submit wrapper. See decisions/021-partner-track-kyc-artifact.md. # Session 64 adds "kyc_artifact_view" for the partner-track π-5 admin read-only # audit-view handler. Distinct from kyc_artifact_submit: submit is the write-side # event (partner shipped an artifact), view is the read-side event (an admin # decrypted the PII). Separating the variants lets audit queries distinguish # "artifact ingested" from "admin examined". See decisions/022-partner-track-admin-audit-view.md. # Session 65 adds the four MCP Tier-6 approval-flow variants. approved/rejected # are the owner's decision; executed is the system running the tool after approve; # expired is the sweep auto-closing a Pending row whose expires_at elapsed. Four # distinct variants (not one mcp_approval_decision) so the audit query shape # "how many Tier-6 calls ever executed vs how many approvals were granted" # reduces to a one-field filter. See decisions/023-mcp-tier6-approval.md. AuditAction = "sign_contract" | "renew_subscription" | "verify_kyc" | "grant_bonus" | "view_document" | "appoint_director" | "remove_director" | "grant_signing_authority" | "revoke_signing_authority" | "governance_step" | "enforce_decision" | "revoke_api_key" | "rotate_api_key" | "ai_query_debit" | "partner_registration_request_submit" | "partner_registration_request_approve" | "kyc_artifact_submit" | "kyc_artifact_view" | "kyc_personal_data_view" | "mcp_approval_approved" | "mcp_approval_rejected" | "mcp_approval_executed" | "mcp_approval_expired" | "mcp_approval_admin_approved" | "mcp_approval_admin_rejected" | "mcp_approval_admin_viewed" | "other" # AuditTargetType — kinds of entities an audited action targets. Deliberately broader than # DocumentKind: audit targets include non-document entities (user, wallet, kyc_session). # Session 50 adds 3 DocumentKind-aligned variants (invoice, business_document, share_transfer) # for can_view-consumer audit rows — "contract" was already present from session 49. These # carry an orthogonal VisibilityDecision sub-record for the visibility-specific fields # (audience_sid, matched_role_sids, classification_intersected, as_of_epoch, …). # Session 53 adds "company_role" so appoint_director / remove_director can target the # CompanyRole row by sid (rather than the company or subject by proxy). # Session 54 adds "signing_authority" so designate_signatory / revoke_signatory can target # the SigningAuthority row by sid. # Session 55 adds "governance_action" so governance_step / enforce_decision can target # the GovernanceAction row by sid. # Session 57 adds "api_key" so revoke_api_key / rotate_api_key can target the ApiKey # row by sid. # Session 59 adds "ai_call" so ai_query_debit can target a specific upstream AI query; # target_ref carries the external call_id so audit rows cross-link to aibroker's log. # Session 63 adds "kyc_artifact" so kyc_artifact_submit targets the attestation row # (never the payload row — the audit chain follows the immutable side). See D-021. # Session 65 adds "mcp_approval" so the four mcp_approval_* variants target the # McpApproval row by sid. Target is always the approval, never the underlying # tool's output entity (forensics want the approval chain to be traceable as one # unit). See decisions/023-mcp-tier6-approval.md. AuditTargetType = "contract" | "invoice" | "business_document" | "share_transfer" | "subscription" | "kyc_session" | "user" | "wallet" | "company" | "company_role" | "signing_authority" | "governance_action" | "api_key" | "ai_call" | "registration_request" | "kyc_artifact" | "kyc_personal_data" | "mcp_approval" | "other" # Business document type BusinessDocumentType = "certificate_of_incorporation" | "business_license" | "memorandum" | "articles" | "resolution" | "other" # Company role kind — who holds what position in a company/coop. # M1 ships: shareholder (FZC investor), founding_member / platinum_member (coop tiers), # founder (creator marker, always written alongside the creator's first substantive role), # director (company governance role; M&A Art. 17/18 appointment/removal by shareholders' # Ordinary Resolution — Q1 pulled forward from M2 per Alex 2026-04-22). # M2 will add: "officer" | "liquidator" — additive, no schema break. CompanyRoleKind = "shareholder" | "founding_member" | "platinum_member" | "founder" | "director" # Free Zone Company status CompanyStatus = "draft" | "pending_signatures" | "active" | "inactive" | "suspended" # Company subscription plan for recurring fees CompanySubscriptionPlan = "monthly" | "one_year" | "two_years" # Company types available in the freezone CompanyType = "single_fzc" | "startup_fzc" | "growth_fzc" | "global_fzc" | "coop_standard_fzc" | "coop_platinum_fzc" # Contract status ContractStatus = "draft" | "pending_signature" | "active" | "expired" | "archived" # Credit transaction status CreditTransactionStatus = "pending" | "completed" | "failed" # Credit transaction type CreditTransactionType = "top_up" | "deduction" | "refund" | "bonus" | "expiry" # DecisionOutcome — the ruling in an `issue_decision` call. Re-used for both original- # decision rows (parent is a review) AND appeal-ruling rows (parent is an appeal). # Semantics discriminate by parent: # - On a review-parented decision: the outcome applies to the member (dismiss = # no discipline; warning/probation/suspension = intermediate; remove = expulsion). # - On an appeal-parented decision: `dismiss` means "appeal denied → original stands # → enforcement allowed"; any non-dismiss outcome means "appeal granted → original # set aside → enforcement blocked permanently" (M1 has no automatic re-appeal or # re-ruling flow; restoration is manual if needed). # The re-use keeps the enum tight and lets one handler (`issue_decision`) cover both # original rulings and appeal rulings. Principle-3 pure: outcomes are never mutated; # changes always go via new rows. DecisionOutcome = "dismiss" | "warning" | "probation" | "suspension" | "remove" # Dividend distribution status DividendStatus = "proposed" | "approved" | "processing" | "completed" | "cancelled" # DocumentKind — discriminates which OSIS entity a DocumentAudience row references. # Added in session 48 alongside DocumentAudience. Extend when new document-bearing entity # types ship; a DocumentAudience row's document_type must match its document_ref's entity. DocumentKind = "contract" | "invoice" | "inbox_message" | "business_document" | "proposal" | "resolution" | "tax_filing" | "financial_report" | "expense" | "share_transfer" | "dividend" # Document types for signing DocumentType = "contract" | "agreement" | "invoice" | "certificate" | "other" # Entity draft status (multi-sig creation flow) EntityDraftStatus = "draft" | "pending_signatures" | "all_signed" | "paid" | "active" | "cancelled" | "expired" # ═══════════════════════════════════════════════════════════════════════════════ # ZANZIBAR DIGITAL FREEZONE - OSIS Schema v2.0 # ═══════════════════════════════════════════════════════════════════════════════ # # A complete schema-driven system for digital residency and company management. # Features: # - Type-safe enums for all categorical data # - Full-text search on relevant fields # - Declarative business rules # - Cryptographic identity with key versioning # # ═══════════════════════════════════════════════════════════════════════════════ # ─────────────────────────────────────────────────────────────────────────────── # ENUMS - Type-safe categorical values # ─────────────────────────────────────────────────────────────────────────────── # Entity type for polymorphic references (resident or company) EntityType = "resident" | "company" # Exchange transaction status ExchangeStatus = "pending" | "processing" | "completed" | "failed" | "expired" # ExecutorRole — who may act at each due-process step. Alex Q3 enumerated 9 options. # The coop picks ONE per step at formation (stored in # FreeZoneCompany.due_process_executors); absent that config, handlers fall back to # branding.toml [governance.due_process.default_executors]. Step 5 "arbitration" # is RECORDING-ONLY in M1 per Alex Q9 — handler stores the filing/outcome reference # but does not implement arbitration mechanics. Full on-platform arbitration is M5+. ExecutorRole = "ethics_committee" | "governance_board" | "review_panel" | "rotating_impartial_committee" | "external_mediator" | "random_member_jury" | "general_assembly" | "internal_appeals_committee" | "arbitration" # Expense status ExpenseStatus = "draft" | "submitted" | "approved" | "rejected" # Financial report type FinancialReportType = "balance_sheet" | "income_statement" | "cash_flow" | "trial_balance" # Form field type for dynamic form schemas FormFieldType = "text" | "textarea" | "dropdown" | "number" | "checkbox" | "multi_select" # Fund transfer status FundTransferStatus = "pending" | "processing" | "completed" | "failed" | "cancelled" # GovernanceActionKind — the 5 fixed due-process steps from Coop Bylaws (session 55, # Alex Q3 resolved 2026-04-22, #308#22569). One GovernanceAction row per step; rows # chain via parent_action_sid: notice → response → review → decision → appeal. An # appeal RULING is itself a `decision` row whose parent points at the appeal row — # the parent's kind discriminates which executor set applies (Step-4 for review- # parented decisions; Step-5 for appeal-parented decisions). See decisions/013. GovernanceActionKind = "notice" | "response" | "review" | "decision" | "appeal" # Invoice status InvoiceStatus = "draft" | "sent" | "paid" | "overdue" | "cancelled" | "final" # KYC verification status KycStatus = "pending" | "in_progress" | "completed" | "failed" | "expired" # MCP approval status enum. # Session 65 adds "approved_executed" as a fifth terminal state: approved is the # owner's decision *before* the tool actually runs; approved_executed is after # successful dispatch. Splitting the two states makes the approve handler # crash-safe — a crash between status-flip and tool dispatch leaves the row in # Approved (not ApprovedExecuted), and a re-run of the approve handler resumes # from Approved without a second decision. D-011 closure primitive applies: # Pending → Approved → ApprovedExecuted | execution_error set on row. # See decisions/023-mcp-tier6-approval.md. McpApprovalStatus = "pending" | "approved" | "approved_executed" | "rejected" | "expired" # Message categories MessageCategory = "account" | "registration" | "security" | "kyc" | "payment" | "company" | "resident" | "general" # Message types MessageType = "system" | "notification" | "alert" # ─────────────────────────────────────────────────────────────────────────────── # ONBOARDING TYPES - Progressive unlock tiers # ─────────────────────────────────────────────────────────────────────────────── # Step status in the onboarding journey OnboardingStepStatus = "locked" | "available" | "completed" # Payment method enum PaymentMethod = "crypto" | "clickpesa" | "bank_transfer" | "card" # Payment status PaymentStatus = "pending" | "confirming" | "confirmed" | "failed" | "expired" # Payment types PaymentType = "bitcoin" | "ethereum" | "clickpesa" | "stripe" | "bank_transfer" # Proposal category ProposalCategory = "financial" | "strategic" | "operational" | "governance" | "personnel" # Proposal status ProposalStatus = "draft" | "active" | "passed" | "rejected" | "expired" | "executed" # Registration contract categories RegistrationContractCategory = "terms_of_service" | "privacy_policy" | "digital_residency" | "company_formation" # Registration flow status RegistrationFlowStatus = "identity_pending" | "identity_complete" | "personal_info_complete" | "payment_pending" | "payment_complete" | "kyc_pending" | "kyc_complete" | "registration_complete" # Registration request status for reseller-submitted registrations RegistrationRequestStatus = "pending" | "under_review" | "approved" | "rejected" | "needs_info" # Registration request type (resident or company) RegistrationRequestType = "resident" | "company" # Digital Resident status ResidentStatus = "pending" | "active" | "suspended" | "expired" # Resolution kind (session 56 / D-014). Drives threshold selection from # branding.toml [governance.resolution_thresholds] at create_proposal time. # Ordinary = simple majority (> 50%); Special = super-majority (>= 75%); # Board = board-of-directors resolution (not a shareholder vote; consumed by # designate_signatory to authorise a delegated SigningAuthority grant). ResolutionKind = "ordinary" | "special" | "board" # AML/KYC risk tier (session 67, D-025 forward-compat per D-024 invariant 6). # Populated by M2 ComplianceReview (#345) from iDenfy AML risk score per Compliance # WP §3.B (70-100 Low / 41-69 Medium / 0-40 High; PEP is a separate screening flag). # Drives scheduled-refresh cadence per WP §3.C (Low=annual, Medium=biannual, High/PEP=quarterly). # No M1 logic reads this; ships Option-None on every row so M2 doesn't backfill. RiskTier = "low" | "medium" | "high" | "pep" # Share transfer status ShareTransferStatus = "pending" | "approved" | "rejected" | "completed" | "cancelled" # Shareholder invite status ShareholderInviteStatus = "pending" | "accepted" | "rejected" | "expired" # ─────────────────────────────────────────────────────────────────────────────── # SUBSCRIPTION TYPES - Resident subscription lifecycle # ─────────────────────────────────────────────────────────────────────────────── # Subscription status (computed by backend from dates) SubscriptionStatus = "none" | "active" | "expiring" | "suspended" # Tax filing period type TaxFilingPeriod = "monthly" | "quarterly" | "annual" # Tax filing status TaxFilingStatus = "draft" | "submitted" | "accepted" | "rejected" | "amended" # Treasury account kind TreasuryAccountKind = "crypto_wallet" | "bank_account" # Treasury transaction status TreasuryTransactionStatus = "pending" | "confirmed" | "failed" # Treasury transaction type TreasuryTransactionType = "credit" | "debit" | "transfer" # Verification link status VerificationLinkStatus = "active" | "expired" | "used" | "revoked" # Vote value VoteValue = "for" | "against" | "abstain" # Voting model for companies VotingModel = "standard" | "cooperative"