> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jedify.com/llms.txt
> Use this file to discover all available pages before exploring further.

# First run

> What to settle after a healthy-looking install and before your first build: CORS, cold starts that look like CORS errors, your organization's account record, your data warehouse's network and authentication, and checking the first build.

A finished install passes every health check and can still fail the first time someone uses it. Each item on this page fails late, after the deploy looks done, and most of them fail in the browser, where no server-side test sees them. Work through them in order before your first build.

## Allow your origin for CORS

The API answers cross-origin requests only from the origins in `CORS_WHITELISTED_URLS`, in `values/jedify-be.yaml`. With `ENV=production`, the API's built-in list holds Jedify's own domains, not yours.

The [install](/self-hosted/gcp/install) set `CORS_WHITELISTED_URLS` to your application host, `https://<your application host>`, which is the origin the web app runs on.

If it's wrong, the page loads and the user signs in, and then the browser blocks every API call. Nothing fails on the server, and the same calls answer `200` to `curl`.

To verify, send a browser-style preflight request from your origin. Expected: `200`. An origin that isn't allowed gets `400`.

```bash theme={null}
curl -sS -o /dev/null -w '%{http_code}\n' -X OPTIONS "https://$APP_HOST/api/readiness" \
  -H "Origin: https://$APP_HOST" -H 'Access-Control-Request-Method: GET'
```

To change it, edit the `CORS_WHITELISTED_URLS` entry in `values/jedify-be.yaml`, then upgrade both application releases:

```bash theme={null}
helm upgrade jedify-be "./jedify-be-$JEDIFY_VERSION.tgz" \
  --namespace jedify-be -f values/jedify-be.yaml
helm upgrade jedify-be-workers "./jedify-be-$JEDIFY_VERSION.tgz" \
  --namespace jedify-be -f values/jedify-be.yaml -f values/jedify-be-workers.yaml
```

## A cold start can look like a CORS error

Two kinds of slowness follow every rollout:

* **The first authenticated request of each new pod** takes around 80 seconds: its caches are cold, and it makes its first round trip to Zitadel's management API. After that, requests take well under a second. One slow first call after a rollout isn't a fault.
* **The load balancer lags behind the pods.** A new pod is ready before the load balancer's own health checks mark it healthy, 30 to 90 seconds later.

In those windows the load balancer itself can answer a request with a `502`, `503` or `504`. A response from the load balancer carries **no `Access-Control-Allow-Origin` header**, so the browser's console fills with `No 'Access-Control-Allow-Origin' header is present` for endpoints whose CORS is configured correctly.

Before you change any CORS setting, check the status code. In the browser's network panel, look at the failing request's status; or call the same endpoint with `curl` and your origin:

```bash theme={null}
curl -sS -o /dev/null -D - "https://$APP_HOST/api/readiness" -H "Origin: https://$APP_HOST"
```

If it answers `200` with an `access-control-allow-origin` header, CORS is fine and the earlier failure was timing. Retry after a minute.

## Create the account record

<Warning>
  This is a manual step today, and the first build fails without it.
</Warning>

A Zitadel organization isn't yet a Jedify account. When someone signs in, your Zitadel organization's ID becomes their account ID, but nothing creates the matching account record. So the first build, or the first question, fails in the launcher component **after** sign-in succeeded:

```text theme={null}
AccountNotFound: no such account <your organization ID>
```

The record lives in the `accounts` collection of the application's database. It needs:

| Field | Value |
| - | - |
| `account_id` | Your Zitadel organization's ID |
| `brief` | Required |
| `business_model` | Required |

To find your organization's ID:

```bash theme={null}
gcloud secrets versions access latest --secret "${PREFIX}-app-config" --project "$PROJECT_ID" \
  | jq -r .ZITADEL_ORG_ID
```

Create the record with `scripts/account-setup.sh` from the bundle, which ships it from release 2026.10.12. It writes the record from inside the API pod, through the application's own model. Write one or two sentences about your company for `--brief`: the application adds them to its prompts as company context. `--business-model` is one of `B2B`, `B2C`, `B2G`, `C2C` or `B2B2C`, and defaults to `B2B`. The script is safe to run twice.

```bash theme={null}
./scripts/account-setup.sh --org-id "<your organization ID>" \
  --brief "<one or two sentences about your company>" --business-model B2B
```

Expected: `created <your organization ID>`, or `already exists <your organization ID>` when the record is already there. Add `--context <name>` to use a kubectl context other than the current one, and `--namespace` if the application isn't in `jedify-be`.

To check for the record without writing anything, run it with `--check` instead of `--brief`. It exits `0` when the record exists and `1` when it doesn't.

```bash theme={null}
./scripts/account-setup.sh --org-id "<your organization ID>" --check
```

<Accordion title="Releases before 2026.10.12">
  The bundle doesn't include the script. Run the same command in the API pod directly:

  ```bash theme={null}
  ORG_ID="<your organization ID>"
  BRIEF="<one or two sentences about your company>"
  BUSINESS_MODEL="B2B"
  kubectl -n jedify-be exec -i deploy/api -- \
    env ORG_ID="$ORG_ID" BRIEF="$BRIEF" BUSINESS_MODEL="$BUSINESS_MODEL" python - <<'EOF'
  import os
  from entities.account import Account

  org = os.environ["ORG_ID"]
  if Account.objects(account_id=org).first():
      print("already exists", org)
  else:
      Account(account_id=org, brief=os.environ["BRIEF"], business_model=os.environ["BUSINESS_MODEL"]).save()
      print("created", org)
  EOF
  ```

  Expected: `created <your organization ID>`, or `already exists <your organization ID>`.
</Accordion>

To verify after the record exists, check the launcher's log for the error. Expected: `0`.

```bash theme={null}
kubectl -n jedify-be logs deploy/launcher --since=24h | grep -c 'AccountNotFound'
```

## Connect your data warehouse

### Network

Your warehouse sees **your installation's** addresses, not Jedify's. The IP addresses listed on the hosted service's connector pages don't apply to a self-hosted installation.

* **Through Cloud NAT** (a network OpenTofu created): allow the NAT's addresses. Cloud NAT allocates them automatically, so check them again after any change to the NAT.

  ```bash theme={null}
  gcloud compute routers get-status "${PREFIX}-router" \
    --region "$REGION" --project "$PROJECT_ID" --format='yaml(result.natStatus)'
  ```

  The addresses are under `autoAllocatedNatIps`.
* **Through your own network's egress** (an existing network): ask your network team which addresses traffic leaves from.
* **Over a private connection** to the warehouse: with [egress masquerade](/self-hosted/gcp/requirements#egress-masquerade) on, allow your node subnet; with it off, allow your pod range.

Your egress firewall must also allow the warehouse's own hosts. For Snowflake, run this in Snowflake and allow every host it returns, on the port it lists:

```sql theme={null}
SELECT SYSTEM$ALLOWLIST();
```

That includes the account host and its stage hosts on port 443, and the OCSP hosts on **port 80**. Snowflake's drivers check certificate revocation over plain HTTP on port 80, so a firewall that allows only 443 still stalls the connection.

A blocked call from the cluster hangs instead of failing fast, so a missing allowlist entry shows up as a connection test or a build that never finishes.

### Authentication

* **Snowflake.** Use key-pair authentication with a dedicated service user, exactly as [Snowflake Data Connector](/settings/data-connectors/snowflake) describes. Skip its step about allowlisting Jedify's IP addresses, and allow your own as described above.
* **BigQuery.** The connector takes an uploaded credentials file: a service-account key, or a Workload Identity Federation configuration (`external_account`). It can't use the pod's own Workload Identity, even though the pods run as a Google service account. If you don't want a long-lived key, use a Workload Identity Federation configuration. See [BigQuery Data Connector](/settings/data-connectors/bigquery).
* **A warehouse behind a private certificate authority.** Turn on `trustExtraCA`, as [A gateway behind a private certificate authority](/self-hosted/gcp/model-gateway#a-gateway-behind-a-private-certificate-authority) describes; it covers every endpoint the application calls.

## Run the first build and check it

<Steps>
  <Step title="Confirm the background tasks are scheduled">
    The application's periodic tasks run as CronJobs. Without them, regression runs never leave `PENDING`, and stuck jobs are never cleaned up.

    ```bash theme={null}
    kubectl -n jedify-be get cronjobs
    ```

    Expected: `automated-agents`, `check-component-jobs`, `kill-hanging-edits`, `kill-processing-inquiries`, `manage-regression-tests` and `refresh-semantic-dimensions`, each with a recent `LAST SCHEDULE` (the daily one after its first run).
  </Step>

  <Step title="Connect your warehouse and build">
    Sign in as a user with the `Tenant Admin` role. Connect your warehouse under **Settings → Data Connectors** (see [Data Connectors](/settings/data-connectors/overview)), then add the tables Jedify should learn from to the Semantic Catalog (see [Adding tables](/semantic-fusion/catalog/adding-tables)). Jedify builds your Semantic Fusion™ model from them.
  </Step>

  <Step title="Check the logs while it builds">
    ```bash theme={null}
    for d in launcher taxonomy-builder grounding-worker; do
      echo "== $d"
      kubectl -n jedify-be logs "deploy/$d" --since=1h \
        | grep -E 'AccountNotFound|Invalid model name|No groundable columns|CERTIFICATE_VERIFY_FAILED|WORKER TIMEOUT' | tail -5
    done
    ```

    Expected: nothing under any heading. Each pattern has an entry in [Troubleshooting](/self-hosted/gcp/troubleshooting).
  </Step>

  <Step title="Ask a first question">
    Ask a narrow question you know the answer to, such as a count for one day, and check both the answer and the SQL. See [Ask Jedify](/using-jedify/ask-jedify).

    Expected: progress appears live while the question runs, and the answer matches your source. If progress doesn't appear until you reload the page, realtime isn't connected: see [Realtime](/self-hosted/gcp/troubleshooting#realtime). If the question stays in progress for ever, check the models: see [Verify the models](/self-hosted/gcp/model-gateway#verify-the-models).
  </Step>
</Steps>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.