Process: Add a Client
Process: Add a Client
Owner: Client Intake Manager (Camille) Type: Manual (semi-automated) Frequency: Per new client (commercial or internal/personal)
Purpose
Provision a new client across the three systems the factory uses, in one consistent pass:
- Factory record — the client profile in the repo (
production-lines/clients/{slug}/) - Atlassian / JSM — the client as an Organization in the Jackson Creek Tech service desk, so they can raise and track requests
- JCT portal — the client's delivery zone at
{slug}.jacksoncreektech.ca(Cloudflare Pages + Access)
Not every client needs all three at once. Part 1 is always done. Parts 2 and 3 are done when the client will actually raise tickets / receive published deliverables. Internal/personal "clients" (e.g. dogfooding) usually do Part 1 only, and defer 2–3.
Overview
[Decision: commercial or internal?] → Part 1 Factory record (always)
→ Part 2 Atlassian/JSM (if client raises tickets)
→ Part 3 JCT portal (if client receives published deliverables)
Client registry
Client IDs are sequential CLIENT-NNN, assigned in Part 1.
| ID | Slug | Name | Type |
|---|---|---|---|
| CLIENT-001 | stm | Société de transport de Montréal | Commercial |
| CLIENT-002 | bruno-bock | Bruno Bock (personal) | Internal |
Add a row here each time a client is created. The highest existing ID + 1 is the next CLIENT-NNN.
Part 1 — Factory record (always)
1.1 Choose the slug
- kebab-case, short, becomes the folder name and the future portal subdomain (
{slug}.jacksoncreektech.ca). - Examples:
stm,transgesco,bruno-bock(subdomain may be shortened, e.g.bb— record the chosen subdomain in the profile if it differs from the slug).
1.2 Assign the next CLIENT-NNN
Scan the registry table above; take the highest CLIENT-NNN and increment. Zero-pad to 3 digits.
1.3 Create the profile
production-lines/clients/{slug}/profile.md
Copy the structure of an existing profile (production-lines/clients/stm/profile.md for commercial, production-lines/clients/bruno-bock/profile.md for internal). Fill:
- Client ID, slug, last-updated date
- Organization (full name, segment, size, location, language)
- Business context (one paragraph)
- Projects / Work Orders tables (start empty or with the triggering WO)
- A Planned table for deferred items (portal, JSM org) so nothing is lost
1.4 Add the registry row
Add the new client to the registry table in this file.
Output: production-lines/clients/{slug}/profile.md exists, registry updated.
Part 2 — Atlassian / JSM organization (if the client raises tickets)
The client raises and tracks requests as an Organization in the Jackson Creek Tech service desk.
| Fact | Value |
|---|---|
| Site | jackson-creek-tech.atlassian.net |
| Service desk project | JCT |
| Service desk id | 3 |
| Internal factory project (backlog/epics) | UAA — not used for clients; leave alone here |
Scope note: clients live in JCT (service desk). The factory's own backlog lives in UAA. Do not put a client into UAA.
2.1 Get the cloudId
Use mcp__claude_ai_Atlassian__getAccessibleAtlassianResources (first run) to resolve the cloudId for jackson-creek-tech.atlassian.net.
2.2 Create the organization
JSM organizations are created via the service desk REST API (orgs were provisioned by API; groups/types/automations/workflow remain UI-only — see project_jsm-setup-status).
# Auth: Basic with Atlassian account email + API token (id.atlassian.com → API tokens)
# Create the organization
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST "https://jackson-creek-tech.atlassian.net/rest/servicedeskapi/organization" \
-H "Content-Type: application/json" \
-d '{"name": "{Client full name}"}'
# → returns the new organization id
2.3 Associate the organization to the JCT service desk
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST "https://jackson-creek-tech.atlassian.net/rest/servicedeskapi/servicedesk/3/organization" \
-H "Content-Type: application/json" \
-d '{"organizationId": {orgId}}'
2.4 Add the client's contacts as customers
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST "https://jackson-creek-tech.atlassian.net/rest/servicedeskapi/organization/{orgId}/user" \
-H "Content-Type: application/json" \
-d '{"usernames": ["[email protected]", "[email protected]"]}'
If a contact is not yet a customer, JSM creates them. They receive an invite to the portal.
2.5 UI fallback
If the API is unavailable: jackson-creek-tech.atlassian.net → Service project (JCT) → Customers → Organizations → Add organization → add the contacts. Same result.
2.6 Record it
In the client profile, replace the deferred "JSM org" row with the live org id and the list of authorized contacts. Requests later raised by the client get a jira_key (JCT-NNN); link that into the matching repo WO/REQ frontmatter so /request-sync can track status.
Output: JSM organization exists, associated to JCT, contacts added, profile updated.
Part 3 — JCT portal (if the client receives published deliverables)
The portal scaffold + Cloudflare steps are already documented in detail. Do not duplicate — follow:
C:\Projects\jct-portail-template\ADD-CLIENT.md
Summary of that checklist (≤ 45 min):
gh repo create bockbr/jct-portail-{slug} --template bockbr/jct-portail-template --private- Customize palette (
assets/style.css:root) + hub (index.html) - Commit + push
- Cloudflare Pages → Connect to Git → build preset None, output
/(dashboard only) - Custom domain
{slug}.jacksoncreektech.ca(dashboard only) - Cloudflare Access → Self-hosted app, OTP, allow
[email protected]+ client contacts (dashboard only) - Incognito access test (blocking DoD)
Steps 4–6 are Cloudflare dashboard only — they cannot be scripted from here. Steps 1–3 can be run via
gh/git.
After go-live, record the portal URL + Cloudflare Pages/Access links in the client profile (replace the deferred "portal" row).
Output: {slug}.jacksoncreektech.ca live and access-tested.
Quality gate
A client is fully added when (scope-dependent):
- Part 1 —
production-lines/clients/{slug}/profile.mdexists; registry row added - Part 2 (if applicable) — JSM organization created, associated to JCT, contacts added, recorded in profile
- Part 3 (if applicable) — portal live, access tested, links recorded in profile
- Deferred parts (if any) are logged in the profile's Planned table — nothing lost
Notes & gotchas
- Internal/personal clients (CLIENT-002 Bruno Bock pattern): Part 1 only; Parts 2–3 deferred and logged. Used to flow personal/dogfooding talents through the same WO-PROD machinery while staying separated from real clients.
- Slug vs subdomain: usually identical; if shortened (e.g.
bruno-bock→bb), record both in the profile so the portal step uses the right subdomain. - OTP not arriving (Part 3): 90% antispam — whitelist
[email protected]. - Don't cross the streams: clients = JCT service desk; factory internal backlog = UAA. Keep them separate.