3. Configuration¶
← Installation · Next: Running the System →
This page covers everything you configure: your Google Cloud project, the database's config tab (your personal settings and AI model choices), and the AI provider credentials.
Google Cloud setup¶
This expands on Step 3 of the Installation Guide. Maestro reads and writes Google Sheets and stores files in Google Drive on your behalf, using a service account — a non-human Google identity with its own key.
Create the project and enable APIs¶
- Open https://console.cloud.google.com and sign in.
- Top bar → project dropdown → New Project. Name it
maestro. Create and select it. - Enable these APIs one at a time (search each by name in the top search bar, open it, click Enable):
- Google Sheets API — read/write the database
- Google Drive API — store resume/cover-letter files and folders
- Google Docs API — apply formatting to converted documents
Create the service account and key¶
- APIs & Services → Credentials → Create Credentials → Service account.
- Name it
maestro-pipeline. Click Create and Continue, then Done (skip the optional role and access steps). - Click the service account you just made → Keys tab → Add Key → Create new key → JSON → Create.
- A
.jsonfile downloads. This is a secret — treat it like a password. Don't put it in a public folder or commit it to Git.
Values you'll extract from the JSON¶
Open the downloaded JSON in a text editor. Two fields matter:
| JSON field | Used as | Where |
|---|---|---|
client_email |
GOOGLE_SHEETS_CLIENT_EMAIL |
dashboard .env.local; also shared into the Sheet & Drive folder |
private_key |
GOOGLE_SHEETS_PRIVATE_KEY |
dashboard .env.local (and the n8n Google credential) |
⚠️ The single-service-account rule. Both the dashboard and the n8n pipeline must authenticate as the same service account. The pipeline creates the Drive folders; the dashboard writes files into them. If they're different accounts, the dashboard can't write to the pipeline's folders and you'll get 403 Forbidden errors on "Save to Drive."
Share access¶
The service account can't see anything until you share it in:
- The database Sheet — open it → Share → paste
client_email→ Editor. - The Drive root folder — create a folder for Maestro's output, share it with
client_emailas Editor, and use its ID asROOT_FOLDER_IDin the backend.env.
The config tab¶
All of your personal settings live in the config tab of the database Sheet. Each row is one setting. The columns are:
| Column | Meaning |
|---|---|
category |
Grouping (e.g. "Applicant Identity", "LLM - Resume Drafter") |
parameter |
The setting's name (this is the key the software reads) |
value |
What you set |
description |
What it does |
examples_or_notes |
Hints |
display_order, input_type, options |
Used by the dashboard to render the settings nicely |
You edit the value column. Below are the settings that matter most.
Your identity¶
These appear on your resume header.
| Parameter | Example |
|---|---|
applicant_first_name |
Jane |
applicant_last_name |
Doe |
applicant_credentials |
PhD (optional) |
applicant_email |
jane.applications@gmail.com |
applicant_phone |
(555) 123-4567 |
applicant_location |
Seattle, WA |
applicant_linkedin_url |
https://linkedin.com/in/jane-doe |
💡 Many people use a dedicated email for job applications. Set whatever address you want on the resume here.
What you're targeting¶
These drive target fit scoring and discovery.
| Parameter | Example |
|---|---|
target_titles |
Senior Product Manager, Director of Product |
target_location |
Seattle, WA |
target_country |
USA |
domain_interests |
robotics, autonomous systems, saas |
employment_type_considered |
full_time |
Your background (for background-fit scoring)¶
| Parameter | Example |
|---|---|
background_titles |
VP Product, Head of AI |
profile_summary |
A short paragraph describing your career identity |
📌 Keep target and background separate.
target_*describes the role you want;profile_summary/background_titlesdescribe what you have. Mixing them collapses the two fit scores into one. (This was a real bug the project fixed — see Architecture.)
The master document¶
Your full career history feeds every resume draft. It lives in the master_doc tab as key/value rows (the dashboard's Master Doc page edits it). Think of it as your "everything" resume — the agents pull only the relevant parts per job and are forbidden from inventing anything not in it.
Pipeline controls¶
| Parameter | Values | Effect |
|---|---|---|
enable_refinement |
TRUE / FALSE |
Whether Agent 6 refines the resume after the critic |
enable_cover_letter |
TRUE / FALSE |
Whether cover letters are generated |
discovery_max_jobs_per_run |
a number | How many top jobs get fully scored per discovery run |
discovery_rank_title_hard_floor |
a number (e.g. 25) |
Minimum rank score to survive the first-pass ranker |
job_search_cron |
a cron expression | How often the scheduler runs discovery |
timezone |
e.g. America/Los_Angeles |
Timezone for the schedule |
💡
job_search_cronuses standard cron syntax.0 7 * * 1means "7:00 AM every Monday." Tools like crontab.guru help you build these.
Choosing AI models per agent¶
Every agent has three config keys: which provider, which model, and a max-token cap. You can use different providers for different agents (e.g. a cheap model for drafting, a stronger one for refining).
| Agent | Provider key | Model key | Max-tokens key |
|---|---|---|---|
| 1 — Resume Builder | provider_resume_drafter |
model_resume_drafter |
max_tokens_resume_drafter |
| 2 — Cover Letter | provider_cover_letter_drafter |
model_cover_letter_drafter |
max_tokens_cover_letter_drafter |
| 3 — Resume Verifier | provider_resume_verifier |
model_resume_verifier |
max_tokens_resume_verifier |
| 4 — Cover Letter Verifier | provider_cover_letter_verifier |
model_cover_letter_verifier |
max_tokens_cover_letter_verifier |
| 5 — Critic | provider_critic |
model_critic |
max_tokens_critic |
| 6 — Refiner | provider_refiner |
model_refiner |
max_tokens_refiner |
| 7 — Resume Scorer | provider_resume_scorer |
model_resume_scorer |
max_tokens_resume_scorer |
| 8 — Discovery Scorer | provider_discovery_scorer |
model_discovery_scorer |
max_tokens_discovery_scorer |
| 10 — Job Ranker | provider_discovery_ranker |
model_discovery_ranker |
max_tokens_discovery_ranker |
- Provider is one of
anthropic,openai, orgemini. - Model must be a valid model name for that provider and must exist as a row in the
pricingtab (so cost can be calculated).
⚠️ No silent fallbacks. Maestro deliberately has no default model. If a provider/model key is missing or blank, the agent throws an error rather than quietly using something you didn't choose (and didn't budget for). This is intentional — it prevents surprise charges.
Recommended starting models¶
A sensible cost/quality balance:
- Drafting, verifying, scoring, critic, discovery (Agents 1–5, 7, 8): a fast, cheap model (e.g. Anthropic's Haiku tier).
- Refining (Agents 6, 9) and ranking (Agent 10): a stronger model (e.g. a Sonnet tier), since these benefit most from reasoning quality.
Make sure every model you choose has a matching row in the pricing tab with its per-million-token input/output rates.
AI provider credentials¶
Inside n8n, the agents reach the providers through the Call LLM sub-workflow, which uses Header Auth credentials on its HTTP nodes.
Anthropic¶
- Credentials → Add Credential → Header Auth
- Header name:
x-api-key - Value: your
sk-ant-...key - Assign it to the Anthropic HTTP Request node in Call LLM.
OpenAI (optional)¶
- Header name:
Authorization - Value:
Bearer sk-... - Assign to the OpenAI HTTP Request node.
Gemini (optional)¶
- Header name:
x-goog-api-key - Value: your Gemini API key
- Assign to the Gemini HTTP Request node.
You only need credentials for the providers you actually use in your config.
Environment variables reference¶
Maestro has two halves with two different config files. The backend (n8n in Docker) uses maestro-ai-backend/.env; the dashboard and scheduler share maestro-ai-frontend/.env.local. The variable names differ between halves — match them exactly.
Backend (maestro-ai-backend/.env) — read by n8n¶
| Variable | What it is |
|---|---|
GOOGLE_SHEETS_DATABASE_ID |
The database Sheet's ID (from its URL) |
ROOT_FOLDER_ID |
Google Drive folder where the pipeline creates per-job folders |
N8N_BLOCK_ENV_ACCESS_IN_NODE |
Set to false so n8n nodes can read these env vars |
Dashboard & scheduler (maestro-ai-frontend/.env.local)¶
| Variable | What it is | Default / note |
|---|---|---|
GOOGLE_SHEETS_CLIENT_EMAIL |
Service account email | — |
GOOGLE_SHEETS_PRIVATE_KEY |
Service account private key (one line, with literal \n) |
unescaped at runtime |
GOOGLE_SHEETS_DATABASE_ID |
Same Sheet ID as the backend | — |
GOOGLE_OAUTH_CLIENT_ID |
OAuth client ID for Drive uploads (Step 3) | — |
GOOGLE_OAUTH_CLIENT_SECRET |
OAuth client secret | server-side only |
GOOGLE_OAUTH_REFRESH_TOKEN |
OAuth refresh token from the Playground | server-side only |
N8N_WEBHOOK_BASE_URL |
Base URL the dashboard/scheduler call to trigger the pipeline | http://localhost:5678/webhook |
MAESTRO_WEBHOOK_SECRET |
Shared secret sent as the X-Maestro-Secret header on every webhook call |
server-side only — never prefix with NEXT_PUBLIC_ |
⚠️ Use the production webhook path.
N8N_WEBHOOK_BASE_URLmust end in/webhook, not/webhook-test. The-testpath only works while you have the n8n editor open with the workflow in "listen" mode.🔑 The webhook secret is required for triggering. If
MAESTRO_WEBHOOK_SECRETis blank, the scheduler logs "webhook secret not configured; skipping tick" and never fires discovery, and the dashboard's trigger routes return 503. Set the same value here and in n8n's webhook Header Auth (header nameX-Maestro-Secret). See AI provider credentials for where it lives in n8n.
Scheduler-only (set in docker-compose.yml, not .env.local)¶
| Variable | What it is | Default |
|---|---|---|
CONFIG_REFRESH_MS |
How often the scheduler re-reads job_search_cron/timezone from the Sheet |
600000 (10 min) |
📌 Cross-container webhook URL. When the scheduler runs in its own container,
localhostpoints at the container itself, not n8n. The compose file therefore defaultsN8N_WEBHOOK_BASE_URLtohttp://host.docker.internal:5678/webhook. If the scheduler and n8n share a Docker network, the cleaner value ishttp://n8n:5678/webhook(the service name).🔒 Keep
.env,.env.local, and your service-account JSON out of version control. They are already in.gitignore.