Authentication and API Keys

Cosmic Ephemeris deliberately separates calculation credentials from dashboard sessions. API keys authorize server-to-server calculations; a protected browser session authorizes account-management routes.

API keys

Every /api/astro/* endpoint requires one 64-character hexadecimal API key. Send it using the preferred Bearer header:

Authorization: Bearer YOUR_API_KEY

The equivalent header below is also supported:

x-api-key: YOUR_API_KEY

Secret-handling boundary

Call Cosmic Ephemeris from your backend. Never place an account API key in browser JavaScript, a mobile bundle, analytics, a public repository, or a URL. Production CORS is restricted to Cosmic Ephemeris's own website and is not a client-side secret-management mechanism.

Key lifecycle

  • An account can have up to five active keys, each with a unique label.
  • Each key can carry a project label, development/staging/production environment, exact endpoint scope, optional expiration, and optional standard-unit monthly budget.
  • The dashboard returns plaintext only when a key is generated or regenerated.
  • The database stores only a one-way SHA-256 digest, so the existing plaintext cannot be displayed again.
  • Individual revocation affects only that key. The retained legacy regeneration action revokes every active key before creating one replacement.
  • Generating or rotating a key requires your active dashboard session and a trusted Cosmic Ephemeris browser request.
  • Password change or recovery revokes every active key; create replacements after signing in again.
  • Per-key budgets are guardrails inside the account allowance, reset at the UTC month boundary, and apply to standard synchronous and batch calculations made with that key.
  • Rotate immediately after suspected exposure.

Server-side request

curl https://api.cosmicephemeris.com/api/astro/birth-chart \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $COSMICEPHEMERIS_KEY" \
  -d '{
    "date": "2000-01-01",
    "time": "12:00",
    "timezone": "UTC",
    "lat": 0,
    "lon": 0
  }'

Dashboard sessions

Successful signup creates an active account and fixed eight-hour server-side dashboard session in one transaction, then sends the browser directly to the dashboard. Legacy email verification uses the same eight-hour duration. Later login uses eight hours by default; selecting "Keep me signed in" creates a fixed 30-day session instead. Activity does not extend either expiry. The signed credential is set in a Secure, HttpOnly, SameSite cookie; responses do not expose it to JavaScript, and Cosmic Ephemeris does not store it in localStorage. Dashboard requests send the cookie automatically; protected account changes also carry exact approved-origin evidence and Cosmic Ephemeris's request marker. This session is not accepted by calculation endpoints. Logout, password change, and password reset revoke database-backed sessions. Five failed current-password confirmations revoke only the session making those attempts; the account itself is not locked, so its owner can sign in normally.

Durable batch jobs

/api/jobs uses the same server-side API key as calculation routes. A job accepts one to ten mixed items from birth-chart, natal-analysis, panchang, vargas, vimshottari, planetary-hours, lunar-nodes, moon-phases, planetary-events, house-ingresses, aspect-windows, event-calendar, and natal-report. Every submission requires an 8–128 character Idempotency-Key containing letters, digits, period, underscore, colon, or hyphen. Reusing the key with the identical normalized request returns the existing job; reusing it with different content returns 409.

curl https://api.cosmicephemeris.com/api/jobs \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $COSMICEPHEMERIS_KEY" \
  -H "Idempotency-Key: charts-20260925-001" \
  -d '{"items":[{"operation":"birth-chart","input":{
    "date":"2000-01-01","time":"12:00","timezone":"UTC",
    "lat":0,"lon":0
  }}]}'

For a bounded newline-delimited file, send one job item object per line to POST /api/jobs/import. The body must be UTF-8 application/x-ndjson, no larger than 32 KiB, and contain one to ten non-empty lines. Cosmic Ephemeris does not retain the uploaded file, filename, or original bytes; it retains only the same normalized item inputs and results as a JSON job for 24 hours.

curl https://api.cosmicephemeris.com/api/jobs/import \
  -X POST \
  -H "Content-Type: application/x-ndjson" \
  -H "Authorization: Bearer $COSMICEPHEMERIS_KEY" \
  -H "Idempotency-Key: bulk-20260926-001" \
  --data-binary @jobs.ndjson
  • POST /api/jobs creates a job; 202 means queued and an identical replay returns 200.
  • POST /api/jobs/import creates the same job from bounded NDJSON; CSV, multipart, URLs, and compressed uploads are not accepted.
  • GET /api/jobs lists the newest 20 unexpired jobs created by the exact authenticated API key.
  • GET /api/jobs/{id} returns ordered item states and completed results only for that same key.
  • DELETE /api/jobs/{id} cancels queued work or requests cancellation between running items only for that same key.
  • At most ten active jobs are allowed per account. Inputs and results expire after 24 hours.
  • Moon-phase, planetary-event, house-ingress, aspect-window, and event-calendar jobs retain their synchronous search bounds and fixed 30-second worker timeout; natal-report jobs retain fact-only output and a fixed 15-second timeout.
  • Every item retains the 256 KiB stored-result ceiling; inputs and results expire after 24 hours.
  • Each item reserves one standard monthly unit immediately before execution. Polling, replaying, and cancellation do not reserve additional units.
  • Each item requires its exact astro:<operation> scope or astro:* through execution.
  • GET /api/jobs returns the exact supported_operations, supported_import_media_types, and max_import_bytes limits.

Job states are queued, running, succeeded, partial, failed, or cancelled. Clients should poll the returned status_url with bounded backoff and must not submit a fresh idempotency key merely because a network response was lost. A different or rotated key cannot access or replay the original job.

Signed job-completion webhooks

A signed-in account can configure up to five HTTPS destinations on the Job Webhooks dashboard. Cosmic Ephemeris sends one job.completed event when an owned job first reaches succeeded, partial, failed, or cancelled. The event contains status metadata and a job status path only; it never contains job input or calculation results.

{"id":"DELIVERY_UUID","type":"job.completed","created_at":"2026-09-25T16:00:00.000Z","data":{"job":{"id":"JOB_UUID","state":"succeeded","item_count":2,"completed_count":2,"failed_count":0,"completed_at":"2026-09-25T16:00:01.000Z","status_url":"/api/jobs/JOB_UUID"}}}

The endpoint secret is shown only when the endpoint is created or its secret is rotated. Each POST carries:

  • X-Cosmic-Ephemeris-Event-Id: stable delivery UUID for deduplication.
  • X-Cosmic-Ephemeris-Timestamp: Unix seconds for this attempt.
  • X-Cosmic-Ephemeris-Signature: v1= plus hexadecimal HMAC-SHA256 over <timestamp>.<exact raw body>.
const crypto = require("node:crypto");

function verifyCosmicEphemerisWebhook(rawBody, headers, secret) {
  const timestamp = headers["x-cosmic-ephemeris-timestamp"];
  const supplied = headers["x-cosmic-ephemeris-signature"] || "";
  if (!/^\d+$/.test(timestamp || "")) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = "v1=" + crypto.createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(supplied);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Verify the raw bytes before parsing JSON, enforce a short timestamp tolerance, and deduplicate by event ID. Any 2xx response succeeds. Other outcomes retry on a bounded schedule for at most eight attempts. Cosmic Ephemeris follows no redirects, requires public-only IPv4 DNS on every attempt, and retains delivery state for about 25 hours. Secret rotation applies to future claims. Accept the previous secret only for a brief overlap because an attempt already in flight may finish with it.

The protected Job Webhooks dashboard also shows metadata-only delivery history retained for 25 hours. GET /api/webhooks/deliveries returns 25 newest records by default, supports a bounded opaque cursor and optional endpoint filter, and consumes no calculation units. It never returns receiver bodies, event payloads, job inputs/results, endpoint URLs, leases, or signing secrets.

A signed-in account can pause future events without deleting the endpoint by sending exact JSON {"job_completed_enabled":false} to PATCH /api/webhooks/{endpoint_id}/preferences through the same-origin dashboard session. Pause suppresses queued retries; one request already in flight may finish within five seconds. Resume changes only later job transitions—events missed while paused are never replayed—and neither action rotates or discloses the signing secret.

Login endpoint

POST https://api.cosmicephemeris.com/auth/login

{
  "email": "user@example.com",
  "password": "your-password",
  "turnstileToken": "TOKEN_FROM_THE_LOGIN_WIDGET"
}

Production login and signup both require a single-use Cloudflare Turnstile result bound to the correct widget action and Cosmic Ephemeris hostname. Use the hosted login form for dashboard access; API integrations should authenticate calculation requests with an API key.

Login response

{
  "user": {
    "id": "8dd9b117-803a-4571-9d09-998c8a799682",
    "email": "user@example.com",
    "first_name": "Example",
    "last_name": "Developer",
    "plan": "free"
  }
}

Account protection

  • At least 12 characters and no more than 72 UTF-8 bytes
  • At least one letter and one number
  • A valid Cloudflare Turnstile verification from the signup or login form
  • Account activation is separate from confirmed ownership of the email address

New registration may be temporarily closed during deployment or maintenance. When it is open, successful signup creates the active account and protected session immediately and continues to the dashboard. Signup does not send a verification email and does not prove control of the entered address, so check the spelling carefully. Password recovery sent to that mailbox can later establish address ownership. The verification page remains only for accounts that were pending before this signup change.

Authentication errors

  • 400 invalid signup, verification, reset, or password-policy input
  • 401 missing, malformed, revoked, or unknown API key; invalid dashboard credentials or session
  • 403 inactive legacy account, disallowed browser mutation, or failed login/signup bot verification
  • 409 the email is already active or the submitted legacy credentials do not match
  • 429 quota, per-user concurrency, or edge rate/connection limit
  • 503 closed registration or authentication concurrency/dependency failure; retry only when appropriate