Skip to content

Quickstart - Django

This example assumes an existing Django 5 / 6 + Celery 5 application.

Terminal window
pip install z4j-django[celery]

The [celery] extra pulls the engine adapter AND celery-beat in one shot. Other engines follow the same pattern: [rq], [dramatiq], [huey], [arq], [taskiq]. Use [all] for every engine in one go.

Pip transitively pulls z4j-core and z4j-bare.

Open the brain dashboard, pick a project, navigate to /projects/{slug}/agents, and click new agent. The mint dialog returns two values, both shown ONCE:

  • token → goes into Z4J_TOKEN
  • hmac_secret → goes into Z4J_HMAC_SECRET

Copy both before closing the dialog. The brain stores only their hash; if you lose them you have to mint another agent.

Add the following to your .env:

Terminal window
Z4J_BRAIN_URL=http://localhost:7700 # http:// to a loopback host is fine for dev
Z4J_TOKEN=<token from step 2>
Z4J_HMAC_SECRET=<hmac_secret from step 2>
Z4J_PROJECT_ID=<project slug from URL> # any non-empty string works locally
Z4J_AGENT_NAME=fragmaster-web # optional, displayed in dashboard

Then in settings.py:

INSTALLED_APPS += [
"z4j_django",
]
Z4J = {
"brain_url": env("Z4J_BRAIN_URL"),
"token": env("Z4J_TOKEN"),
"hmac_secret": env("Z4J_HMAC_SECRET"),
"project_id": env("Z4J_PROJECT_ID"),
"agent_name": env("Z4J_AGENT_NAME", default=None),
}

That’s it. No CELERY_APP setting, no Z4J_DEV_MODE, no dev_mode=True kwarg. Auto-detect handles 95% of project layouts. If your celery.py lives somewhere unusual or auto-detect can’t find it, add a fallback:

CELERY_APP = "myproject.celery:app" # only if auto-detect failed
Terminal window
./manage.py runserver # dev
celery -A myproject worker -l info -E # worker (separate process)
# or
systemctl restart gunicorn # prod

The agent opens a WebSocket on boot, advertises its engines in the hello frame, and starts streaming events. The dashboard populates within 2-5 seconds.

runserver - should be silent on the z4j side (one optional INFO line about engines):

April 23, 2026 - 13:30:50
Django version 6.0.4, using settings 'myproject.settings'
Starting development server at http://127.0.0.1:8000/

celery worker - look for the bootstrap line right before Celery’s banner:

INFO:z4j.celery.worker_bootstrap:z4j worker bootstrap: agent runtime started (celery_app=myproject, framework=DjangoFrameworkAdapter)

That line is the proof the engine is attached. If you don’t see it, no tasks will reach the brain.

Run the doctor first — it confirms the agent can reach the brain end-to-end and reports a specific failure if it can’t:

Terminal window
python manage.py z4j_doctor

If gunicorn/uvicorn is your web server, run as the same user the service runs under (sudo -u www-data /srv/.../venv/bin/python manage.py z4j_doctor). All probes should be [OK].

Then in the brain dashboard:

  • /projects/{slug}/agents → your agent should be online, with celery (and celery-beat if installed) in adapters.
  • /projects/{slug}/tasks → enqueue anything; it appears within ~100ms.
  • /projects/{slug}/schedules → if django-celery-beat is your scheduler, your CELERYBEAT_SCHEDULE entries appear (read-write with DatabaseScheduler, read-only with filesystem schedulers).

If you also use RQ in the same Django project:

Terminal window
pip install z4j-rq z4j-rqscheduler

The agent will discover both engines automatically. No additional settings needed.

The Celery app is located by trying these in order (first hit wins):

  1. settings.CELERY_APP (object or "module:attr" / "module.attr" string)
  2. <root>.celery_app package-level attribute (cookiecutter convention - your project’s __init__.py doing from .celery import app as celery_app)
  3. <root>.app package-level attribute
  4. celery.current_app._get_current_object() (any configured app made current by import-time side-effect)
  5. <root>.celery.app submodule attribute

<root> is tried in order: ROOT_URLCONF head, WSGI_APPLICATION head, ASGI_APPLICATION head, BASE_DIR.name. The vast majority of layouts hit one of these.

  • hmac_secret is required - you used the first-boot setup token instead of an agent token. Mint a real agent at /projects/{slug}/agents and copy BOTH token AND hmac_secret.
  • refusing plain ws:// connection to ws://... - you’re connecting to a non-loopback host without TLS. Loopback (localhost/127.0.0.1/::1) is auto-allowed; for any other host, set Z4J_BRAIN_URL=https://... (recommended) or Z4J_DEV_MODE=true (trusted networks only).
  • connection closed during send: received 4002 - two processes are using the same agent token at the same time. The brain accepts only one WebSocket per token and force-closes the older one. Mint a second agent for the second process.
  • no Celery app located in this process (INFO log) - benign in the standard split-process layout. The worker process owns task-lifecycle capture; the web process only misses task.delay() send-events. To capture those too, add CELERY_APP = "myproject.celery:app" to settings.py.
  • No tasks in dashboard, worker boots quietly - the INFO:z4j.celery.worker_bootstrap: agent runtime started ... line should appear right before Celery’s banner. If it’s missing, the agent isn’t running. Check Z4J_* env vars and that your worker is actually invoked as celery worker (the bootstrap signal only fires under that command).
  • Schedules read-only - you are using a read-only beat backend. django-celery-beat with DatabaseScheduler is writable; filesystem-based schedulers are not.

See troubleshooting for more.