Skip to main content

When to use

  • An AI agent (or a user in a fresh terminal) needs a working Codika cko_ API key and can receive email — but can’t open a browser, run OAuth, or visit the dashboard.
  • The user wants to sign up for Codika and start deploying workflows in a single shell session.
  • A CI/CD job needs a short-lived key scoped to one organization, minted without leaving the terminal.
The classic dashboard-paste path (codika login --api-key cko_…) still works — see authentication. This page documents the alternative: OTP-based self-provisioning.

How it works

Two commands per flow. The backend sends a 6-digit code to the email; the CLI sends the code back; the backend mints a cko_ key and returns the raw key exactly once. The CLI saves it as a new profile in ~/.config/codika/config.json and activates it.
┌──────────────┐    signup-request    ┌────────────┐    email (6-digit code)    ┌────────┐
│ codika auth  │ ───────────────────► │  Codika    │ ──────────────────────────►│  User  │
│ signup-request│                      │  backend   │                            │        │
└──────────────┘                      └────────────┘                            └────────┘
                                              ▲                                       │
                                              │   signup-complete (email + code)      │
                                              └───────────────────────────────────────┘
                                                       ┌────────────────┐
                                                       │ cko_ saved in  │
                                                       │ ~/.config/cod… │
                                                       └────────────────┘
OTP security constants:
ConstantValue
Code length6 digits
TTL10 minutes
Failed-attempt lockout5 attempts → code deleted
Min resend interval30 seconds
Per-email cap20 requests / hour
Per-IP cap60 requests / hour

codika auth signup-request

Request an OTP for a brand-new signup.
codika auth signup-request --email <email> [--json]

Options

OptionDescription
--email <email> (required)Email address to register
--base-url <url>Codika API base URL override (default: production)
--api-url <url>Full URL to the cliRequestSignupOtp endpoint (overrides --base-url)
--jsonEmit JSON output

Response (--json)

{ "success": true, "data": { "email": "you@example.com", "expiresInSeconds": 600 } }

Errors

CodeStatusMeaningNext action
EMAIL_REQUIRED / EMAIL_INVALID400Missing or malformed emailFix the flag
USER_ALREADY_HAS_ORGANIZATION409Email already owns an orgSwitch to login-request
OTP_RESEND_COOLDOWN429Request spam (within 30s)Wait details.retryInSeconds
EMAIL_RATE_LIMITED42920+ requests/hour from this emailWait and retry
IP_RATE_LIMITED42960+ requests/hour from this IPWait and retry
INTERNAL500Backend hiccupRetry in a few seconds

codika auth signup-complete

Verify the OTP and, in one atomic flow, create the Firebase Auth user, create the organization (with n8n error workflow + webhook auth credential seeded), and mint a cko_ API key with the 10 default scopes.
codika auth signup-complete --email <email> --code <code> [options]

Options

OptionDescription
--email <email> (required)Email that received the OTP
--code <code> (required)6-digit code from the email
--company <name>Organization name (default: “My Organization”)
--description <text>Optional organization description
--key-name <name>Label for the minted API key (default: “CLI default key”)
--key-expires-in <days>1–365; omit for no expiry
--name <name>Local profile name (auto-derived from org name if omitted)
--base-url <url> / --api-url <url>URL overrides
--jsonEmit JSON output

Response (--json)

{
  "success": true,
  "data": {
    "profileName": "my-organization",
    "organizationId": "org_...",
    "organizationName": "My Organization",
    "isNewUser": true,
    "apiKey": {
      "keyId": "019d...",
      "keyPrefix": "cko_aB12",
      "name": "CLI default key",
      "scopes": [
        "deploy:use-case","projects:create","workflows:trigger",
        "executions:read","instances:read","instances:manage",
        "skills:read","integrations:manage","api-keys:manage","projects:read"
      ],
      "createdAt": "2026-..."
    }
  }
}
The raw key is saved to ~/.config/codika/config.json and does not appear in the JSON output (other than masked) after save. It is only available inside the CLI’s profile.

Errors

CodeStatusMeaningNext action
OTP_INVALID400Wrong code (details.attemptsRemaining)Re-read email, retry
OTP_NOT_FOUND404No pending code for this emailRe-run signup-request
OTP_EXPIRED410Code older than 10 minutesRe-run signup-request
OTP_ALREADY_USED409Code already consumedRe-run signup-request
OTP_PURPOSE_MISMATCH409Crossed signup/login codesRe-run the matching request
OTP_LOCKED_OUT4295+ failed attemptsRe-run signup-request
USER_ALREADY_HAS_ORGANIZATION409Raced with another signupSwitch to login-*
COMPANY_NAME_TOO_LONG400--company > 100 charsShorten
INVALID_EXPIRES_IN_DAYS400--key-expires-in outside 1–365Fix or omit
ORGANIZATION_CREATION_FAILED400–500Backend rejected org creationSurface message, retry
INTERNAL500Backend hiccupRetry

codika auth login-request

Same shape as signup-request, but for existing accounts.
codika auth login-request --email <email> [--json]

Errors specific to login

CodeStatusMeaningNext action
USER_NOT_FOUND404No account for this emailSwitch to signup-request
USER_HAS_NO_ORGANIZATION409Account exists but no orgSwitch to signup-request

codika auth login-complete

Verify the OTP and mint a fresh cko_ key for one of the user’s organizations.
codika auth login-complete --email <email> --code <code> \
  [--organization-id <id>] [--key-name <name>] [--key-expires-in <days>] \
  [--name <name>] [--base-url <url>] [--api-url <url>] [--json]
Each login-complete mints a new key. Previous keys remain valid until revoked from the dashboard.

Multi-org handling

Codika users can belong to multiple organizations. If the user has more than one and --organization-id is not provided, the backend returns:
{
  "success": false,
  "status": 409,
  "error": {
    "code": "MULTIPLE_ORGANIZATIONS",
    "message": "Account belongs to multiple organizations. Specify which one to mint a key for.",
    "nextAction": "Re-run `login-complete` with `--organization-id <id>` for the target org.",
    "details": {
      "organizations": [
        { "id": "org_abc", "name": "Acme" },
        { "id": "org_def", "name": "Beta Corp" }
      ]
    }
  }
}
The agent should surface the list, let the user pick, then re-run with --organization-id <id>.

Other login-specific errors

CodeStatusMeaningNext action
ORGANIZATION_NOT_MEMBER403--organization-id doesn’t match any of the user’s orgsPick from details.organizations
MAX_API_KEYS_REACHED429Org already has 20 active keysRevoke one from the dashboard, retry
USER_HAS_NO_ORGANIZATION409All orgs were deleted between request and completeRun signup-request

Agent recipe — optimistic signup, fallback to login

1. codika auth signup-request --email $E --json
   - success            → prompt user for OTP, go to step 2
   - USER_ALREADY_HAS_ORGANIZATION → go to step 4 (login)
   - any other error    → surface + abort

2. codika auth signup-complete --email $E --code $C --json
   - success            → done
   - USER_ALREADY_HAS_ORGANIZATION → go to step 4 (login)
   - OTP_* recoverable  → reprompt or re-request

3. --- unreached in the optimistic path ---

4. codika auth login-request --email $E --json
   - success            → prompt user for OTP, go to step 5
   - USER_NOT_FOUND | USER_HAS_NO_ORGANIZATION → go to step 1 (signup)

5. codika auth login-complete --email $E --code $C --json
   - success                     → done
   - MULTIPLE_ORGANIZATIONS      → show details.organizations, get user pick, re-run with --organization-id
   - ORGANIZATION_NOT_MEMBER     → same

See also