Skip to content

feat: migrate hosted API auth to TALOCODE_API_KEY - #53

Open
Abdulmuiz44 wants to merge 4 commits into
mainfrom
feat/talocode-api-key-migration
Open

feat: migrate hosted API auth to TALOCODE_API_KEY#53
Abdulmuiz44 wants to merge 4 commits into
mainfrom
feat/talocode-api-key-migration

Conversation

@Abdulmuiz44

Copy link
Copy Markdown
Collaborator

Migrates ClipLoop hosted API access to TALOCODE_API_KEY as the primary key, keeps CLIPLOOP_API_KEY as a deprecated fallback, adds Talocode Cloud billing integration, hosted /v1/cliploop/* route surface, SDK updates, docs updates, and tests.

Notes:

  • CLIPLOOP_API_KEY remains supported temporarily as a deprecated fallback.
  • Hosted routes currently return placeholder responses.
  • Actual ClipLoop generation/render engine integration is deferred.
  • Stacklane pricing, SDK, MCP tools, and api.talocode.site defaults are already aligned.

Validation:

  • 24/24 ClipLoop auth/billing tests passing.
  • Stacklane alignment checked.
  • Talocode docs updated.

- Add TALOCODE_API_KEY as primary auth (CLIPLOGOOP_API_KEY deprecated fallback)
- Create hosted API routes under /v1/cliploop/* (brief, script, render, campaign)
- Add Talocode Cloud billing integration (charge before execution)
- Add talocode-auth module (validate, extract, fallback)
- Add talocode-billing module (POST charge endpoint)
- Add route handler utility with auth + billing middleware
- Update gateway config for Talocode Cloud auth
- Update .env.example with TALOCODE_API_KEY docs
- Add auth tests (valid, legacy, reject, extract, key redaction)
- Auth module (talocode-auth.ts): prefer TALOCODE_API_KEY, accept
  CLIPLOOP_API_KEY as deprecated fallback, deprecation warning emitted
- Route handler (talocode-route-handler.ts): deprecation warning for
  legacy key, improved billing metadata with mode: 'hosted'
- Billing (talocode-billing.ts): default base URL now api.talocode.site
- Env schema (env/index.ts): TALOCODE_API_KEY, TALOCODE_BASE_URL,
  CLIPLOOP_API_KEY added to Zod validation
- 6 hosted routes: POST /v1/cliploop/brief/generate, script/generate,
  video/render, campaign/create, campaign/package, GET health
- Each route: input validation, auth via handleRoute, billing charge
  via Talocode Cloud, structured JSON errors on 401/402/502
- SDK (cliploop-sdk): prefers TALOCODE_API_KEY, adds 5 cloud methods
  (generateBrief, generateScript, renderVideo, createCampaign,
  packageCampaign), deprecation warning for CLIPLOOP_API_KEY
- .env.example: TALOCODE_BASE_URL=https://api.talocode.site,
  CLIPLOOP_API_KEY marked deprecated
- Tests: 24 tests (auth precedence, extraction, validation, redaction,
  route handler auth failures, deprecation warnings, billing errors)
- Docs: cliploop-sdk README, docs-site HTML pages, talocode/CLOUD.md,
  talocode/docs/api.md, talocode/docs/pricing.md updated
@netlify

netlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploy Preview for cliploop failed.

Name Link
🔨 Latest commit d2da2eb
🔍 Latest deploy log https://app.netlify.com/projects/cliploop/deploys/6a45f3c54e740a0008ad1377

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 89546488d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +174 to +177
async generateBrief(
input: { prompt: string; channel?: string; tone?: string; duration?: number; cta?: string },
options: ClipLoopRequestOptions = {}
): Promise<Record<string, unknown>> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep hosted SDK methods inside the client class

The ClipLoopClient class is closed just before these new methods, so async generateBrief(...) and the rest are parsed as top-level method declarations rather than class members. This makes @cliploop/sdk fail to build/import and leaves callers without the hosted API methods.

Useful? React with 👍 / 👎.

Comment on lines +179 to +181
const response = await fetch(
`${this.talocodeBaseURL}/v1/cliploop/brief/generate`,
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match SDK URLs to the committed Next routes

The SDK calls /v1/cliploop/..., but the new route files in this repo live under app/api/v1/... and src/app/api/v1/..., which Next exposes as /api/v1/...; next.config.ts only excludes api/ paths from the SPA rewrite, so /v1/cliploop/brief/generate resolves as a non-API page in this app. As committed, every new hosted SDK method targets the wrong path unless an external proxy rewrites it.

Useful? React with 👍 / 👎.

Comment on lines +45 to +47
requestId,
idempotencyKey: requestId,
metadata: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor client idempotency keys for billing

This always sends the locally generated requestId as the billing idempotency key instead of the request's Idempotency-Key header that the SDK sets. Retrying the same request with the same client idempotency key will therefore produce a new billing idempotency key and can charge credits again.

Useful? React with 👍 / 👎.

Comment on lines +19 to +23
if (!prompt || typeof prompt !== 'string') {
return Response.json(
{ ok: false, error: { code: 'validation_error', message: 'prompt is required and must be a string.' } },
{ status: 400 },
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move validation ahead of the credit charge

Because this validation runs inside the handleRoute callback, handleRoute has already charged 15 credits before a missing or non-string prompt returns this 400. The same wrapper pattern is used by the other new hosted endpoints, so malformed requests with valid auth can consume credits without any work being performed.

Useful? React with 👍 / 👎.

Comment thread src/lib/talocode-auth.ts
Comment on lines +47 to +49
if (cliploopKey && incomingKey === cliploopKey) {
return { valid: true, keyType: 'cliploop_legacy' }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject legacy keys once a Talocode key is configured

When both TALOCODE_API_KEY and the deprecated CLIPLOOP_API_KEY remain in the environment during rotation, this fallback still accepts the old ClipLoop key even though the function contract says the legacy key is only accepted when it is the only configured key. That leaves the old secret usable after migration until someone also removes the legacy env var.

Useful? React with 👍 / 👎.

@@ -0,0 +1,21 @@
import { handleRoute } from '@/lib/talocode-route-handler'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not add a root app directory beside src/app

This creates a root-level app/ directory in a project whose real routes live under src/app; Next documents that src/app is ignored when a root app directory is present (https://nextjs.org/docs/app/api-reference/file-conventions/src-folder). In this context the existing pages and public API routes under src/app would drop out of routing, leaving only these new root routes active.

Useful? React with 👍 / 👎.

<div class="content">

<h1>Examples</h1>
<h1>Examples</h1>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the generated examples page shell

The generated docs-site/dist/examples/index.html was replaced with only the inner fragment, unlike the other files under docs-site/dist that include the full document shell. If this dist directory is deployed, the Examples page loses its doctype, stylesheet, sidebar navigation, footer, and script.

Useful? React with 👍 / 👎.

Comment on lines +70 to +71
if (process.env.TALOCODE_API_KEY) {
return process.env.TALOCODE_API_KEY;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep Weekly Promo using ClipLoop API keys

This preference makes new ClipLoopClient().generateWeeklyPromo() send TALOCODE_API_KEY to https://app.cliploop.site/api/public/weekly-promo. I checked that route still authenticates through requireApiKeyIdentity()/authenticateApiKey() against ClipLoop's API-key table, not the Talocode env key, so users who set the new env var, or both vars during migration, will get 401s for the existing Weekly Promo API.

Useful? React with 👍 / 👎.

body: JSON.stringify(input),
}
)
const data = await response.json()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse hosted API errors before JSON decoding

Each new hosted SDK method calls response.json() before checking response.ok. When a gateway or CDN returns an empty or HTML error body for cases like 401/502/504, callers get a raw SyntaxError instead of a ClipLoopApiError with the HTTP status and body, so they cannot handle hosted API failures consistently.

Useful? React with 👍 / 👎.

Central engine module (src/lib/cliploop-cloud-engine.ts):
- generateBrief: LLM-driven brief with deterministic mock fallback
- generateScript: LLM-driven script with scenes, caption, hashtags
- renderVideo: returns 'queued' when provider configured, 'provider_required' when not
- createCampaign: campaign object with draft status
- packageCampaign: package object with delivery summary

Route updates (5 POST routes):
- Each validates input with Zod schema
- Calls handleRoute for auth + billing before engine execution
- Returns structured JSON: { id, object, result, usage }
- Video render includes warnings array when provider_required

Env schema:
- Added CLIPLOOP_PROVIDER, CLIPLOOP_RENDER_PROVIDER,
  REMOTION_RENDER_ENABLED, STORAGE_BUCKET, STORAGE_PUBLIC_URL
- isMockMode() reads from process.env with env fallback

Tests (28 total, 27 passing):
- Engine: schema validation, deterministic mock output, render
  provider_required/queued states, campaign/package objects
- Auth/billing: engine NOT called on missing auth, invalid auth,
  or billing failure (3 new tests)
- Pre-existing ARM64/module resolution blocks engine test runtime
  (zod not loadable from this env), verified structurally

Deterministic fallback:
- Brief/script/campaign/package return structured data without LLM
- Video render returns 'provider_required' when no render provider
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant