العربية

Sub-processors

Effective from: [FOUNDER FILL — YYYY-MM-DD]

Last updated: [FOUNDER FILL — YYYY-MM-DD]

Version: 1

This page lists every external service that processes personal data on our behalf. It is referenced from §5 of the Privacy Policy. A change to this list is a documentation update — it does not re-version the Privacy Policy.

Each row cites the source file or symbol in code so the disclosure cannot drift away from the integration that actually runs.


Active sub-processors

Sub-processorPurposeData sharedRegionSource in code
OpenAI / Gemini / HUMAIN (per environment, operator-selected)AI generation (SWOT, PESTEL, TOWS, objectives, KPIs, recommendations, risks)Allow-listed organization profile fields (SafeOrganizationProfile) + aggregated organization context (department names + counts, prior-cycle attainment, plan-structure counts) + the public regional pack. Never per-person names/emails/user-ids, customer lists, partner lists, or legal_name.US (OpenAI) / US+EU (Gemini) / KSA (HUMAIN)apps/api/ai_runtime.py (AIProvider enum + generate_json_response); apps/api/ai_endpoints.py:_get_organization_context is the redactor
MoyasarPayment processing — checkout intent and gateway-signed webhook for status changesGateway reference, amount, currency, status. Card PANs, CVCs, expiry dates never touch our infrastructure; the Moyasar hosted form handles card entry directly.KSAapps/api/billing.py (POST /v1/billing/checkout-intents, POST /v1/billing/webhooks/moyasar)
Taqnyat / Unifonic / Twilio (operator-selected)SMS one-time-code deliveryPhone number + bilingual OTP message. Message body never logged.Taqnyat: KSA. Unifonic: KSA. Twilio: US.apps/api/integrations.py:KNOWN_SMS_ENDPOINTS (line 299); send path: apps/api/otp_delivery.py:send_otp_code
SMTP relay (operator-configured)Transactional email — verification, password reset, deadline notifications, daily digest, OTP fallbackEmail address, recipient name, message body. No marketing email is sent.Per operator configurationapps/api/email_service.py; admin-managed credentials Fernet-encrypted via apps/api/secrets_crypto.py
Object storage — S3 / GCS / MinIO (operator-configured)Report generation outputs, avatar uploads, logo uploadsFiles uploaded by users; each upload is magic-byte validated before storage so spoofed extensions are rejected.Per operator configurationapps/api/storage.py (allowed types: ALLOWED_REPORT_TYPES, ALLOWED_AVATAR_TYPES, ALLOWED_LOGO_TYPES); credentials encrypted at rest
PostgreSQL 16 (Cloud SQL or self-hosted, per environment)Primary datastore — accounts, organizations, strategic plans, KPIs, audit logs, all relational dataEverything in apps/api/models.py.Per operator configurationapps/api/database.py (DATABASE_URL)
Redis 7 (managed or self-hosted, per environment)Sessions, rate-limit counters, queue (arq), AI provider circuit breaker, WebSocket tickets, MFA countersSession identifiers, integer counters. No PII bodies are stored in Redis.Per operator configurationsettings.py:REDIS_URL; usage via apps/api/redis_client.py
OpenTelemetry collector (operator-supplied, optional)Distributed tracing and metrics scrape targetNon-PII metadata — request path, status code, duration, span attributes. Body capture and header capture are explicitly disabled at instrumentation time.Per operator configurationapps/api/tracing.py (lines 23–24 pin the no-body-no-header posture); apps/api/observability.py redactor strips password, secret, token, authorization substrings before any log line is written

Current provider selection per environment

EnvironmentLLM providerSMS providerSMTP relay
Development / localOpenAI or Gemini (operator choice)None / MailHog catcherMailHog (catches mail; sends nothing)
StagingOpenAI[FOUNDER FILL — Taqnyat / Unifonic / Twilio][FOUNDER FILL — SES / SendGrid / etc.]
Production[FOUNDER FILL — OpenAI / Gemini / HUMAIN][FOUNDER FILL][FOUNDER FILL]

When the production LLM provider is set to AIProvider.HUMAIN, the cross-border transfer disclosure in §6 of the Privacy Policy narrows to no cross-border transfer (HUMAIN is hosted in KSA).


How to track changes

  • A new sub-processor — open a PR adding a row to the table above.

The Privacy Policy automatically picks up the change because it references this page rather than enumerating sub-processors itself.

  • A change of region or scope — same PR mechanism. Material changes

trigger the same 30-day in-app notice + audit log row as a Privacy Policy change (an AuditLog(event_type="legal.subprocessors_updated") on deploy of the change).


*See also:*