Authentication & Accounts

This article walks through the two things you need before your first API call: obtaining your Curviate API key and connecting a LinkedIn account.

Step 1: obtain your API key

Your Curviate workspace API key is ready as soon as you sign up. It lives in the key chip at the top-right of the dashboard top bar. The chip shows the key prefix (for example cvt_live_...); click the copy icon on the chip to copy the full key to your clipboard. The key is a tenant-scoped secret that works across the REST API, CLI, and MCP server, and you only need one. If you have revoked your key, the chip shows a Generate API key control to mint a replacement; that full key is revealed once, so copy it right away.

The Curviate dashboard top bar with the API key chip and its copy button highlighted

Step 2: understand the key format and keep it safe

PropertyValue
Formatcvt_live_ plus a 33-character suffix
ScopeTenant workspace: all accounts and resources
TTLNon-expiring until rotated
RotationKey chip, menu, Refresh token
StorageServer-side environment variable only

There is only one key format. cvt_live_ is used in every environment, including staging; there is no separate test-key prefix. A token beginning cvt_test_ is not recognised as an API key at all and returns 401 UNAUTHORIZED with the message Invalid or expired session token., which is confusing but simply means "this is not a key".

Security

  • Store the key in an environment variable (CURVIATE_API_KEY). Never put it in client-side code or version control.
  • Rotate immediately on suspected exposure via the key chip's menu, Refresh token (the replacement is shown once). To disable a key entirely, use , then Revoke key.

Step 3: connect a LinkedIn account

Before your API calls can act on LinkedIn, you need to connect a LinkedIn account in the Curviate dashboard.

  1. In the dashboard, navigate to Accounts (/accounts).
  2. Click Connect account. If you have no seats, the modal prompts you to purchase a seat first.
  3. The connect modal offers two authentication methods:
    • Credentials (recommended): enter your LinkedIn email and password. Credentials are encrypted at rest and never logged.
    • Cookie: paste the li_at value from your browser's LinkedIn cookies. The modal includes an inline "How to find my cookies?" guide.
  4. Select your preferred method, fill in the form, and click Connect.
  5. If LinkedIn issues a verification challenge, the dashboard walks you through it: entering a one-time code (from email, SMS, an authenticator app, or WhatsApp), choosing between a code and an in-app approval, approving the sign-in in the LinkedIn mobile app, or picking a Recruiter or Sales Navigator seat. A mobile-app approval is time-limited (a few minutes); if it expires before you approve, the dashboard explains how to retry.
  6. If that LinkedIn identity is already connected to your workspace, the dashboard offers to reconnect the existing account rather than creating a duplicate. One LinkedIn account maps to one connection per workspace.
  7. On success, your account appears in the Accounts list with status active and a copy-able acc_... account ID.

Account ID. The account_id (format acc_...) identifies your connected LinkedIn account in every API call, CLI command, and SDK method. Copy it from the Accounts table, or read it from GET /v1/accounts, at any time.

Seat ID. The seat_id (format seat_...) identifies one seat in your subscription. You only need it when you connect an account from the API or the CLI; the dashboard picks the seat for you. Every seat you own is listed with click-to-copy in the seat table on the Billing page (/billing), and any seat whose Attached account column reads Empty is free to connect into.

Connecting accounts programmatically

You can connect accounts from the API instead of the dashboard, for example to onboard your own customers. POST /v1/auth/intent is the single connect path: you supply the LinkedIn credentials or cookie, and the same endpoint handles both a brand-new connection and re-authenticating an existing one in place. LinkedIn often responds with a verification challenge, so the flow can take more than one call.

First, get your seat ID

A new connect binds the account to an empty seat, so seat_id is required. Seat IDs live in the dashboard: open Billing (/billing) and look at the seat table. Every seat you own is listed there under Seat ID as a seat_... chip; click a chip to copy that seat's full ID to your clipboard.

Pick a seat whose Attached account column reads Empty. That is the seat to connect into. A seat that already names an account is taken, and connecting a new account into it is not possible while that account holds it.

If no seat reads Empty, use Add Seats on the same page before you connect. A connect with no empty seat available returns 402 PAYMENT_REQUIRED.

Then run the connect

The credential fields are nested objects, not top-level keys. A body with email, password, or li_at at the top level is rejected:

400 INVALID_REQUEST
credentials required when auth_method=credentials; cookie.li_at required when auth_method=cookie
# Credentials
curl -X POST https://api.curviate.com/v1/auth/intent \
  -H "Authorization: Bearer cvt_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "seat_id": "seat_YOUR_SEAT_ID",
    "auth_method": "credentials",
    "credentials": { "email": "you@example.com", "password": "YOUR_PASSWORD" }
  }'
# Cookie. user_agent is top-level and required for this method.
curl -X POST https://api.curviate.com/v1/auth/intent \
  -H "Authorization: Bearer cvt_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "seat_id": "seat_YOUR_SEAT_ID",
    "auth_method": "cookie",
    "cookie": { "li_at": "YOUR_LI_AT_COOKIE" },
    "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
  }'

To reconnect an existing account instead, add "account_id": "acc_..." to the same body. seat_id is then ignored, because the account keeps the seat it already holds.

The flow in full:

  1. POST /v1/auth/intent with either auth_method: "credentials" (a credentials object holding email and password) or auth_method: "cookie" (a cookie object holding li_at). Cookie auth also requires a top-level user_agent, the exact browser User-Agent the cookie was captured with. Omit account_id to connect a new account into the given seat_id; include account_id to re-authenticate an existing account in place.

  2. A 201 (new) or 200 (reconnect) means the account connected immediately. A 202 means a challenge is required, and the body carries an account_id, a challenge_type, and an expires_at:

    {
      "object": "checkpoint",
      "status": "checkpoint_required",
      "account_id": "acc_...",
      "challenge_type": "two_factor_app",
      "expires_at": "2026-06-08T10:10:00Z"
    }
  3. Resolve the challenge on the same account. For a code-based challenge, submit the code to POST /v1/auth/checkpoint/solve with { "account_id": "acc_...", "code": "123456" }; re-request the notification with POST /v1/auth/checkpoint/request (same body shape) if the user never received it. For a mobile_app_approval challenge, the user approves the sign-in in the LinkedIn app, so poll POST /v1/auth/checkpoint/poll with { "account_id": "acc_..." } until it returns the connected account, or until expires_at passes and it reports status: "expired". You can also read the connect's current status at any time with GET /v1/auth/sessions/{session_id}, passing the connect's account_id (the acc_... id) as session_id.

  4. A resolved challenge returns the connected account. If it turns out to reactivate an account you had previously disconnected, that account is returned with "recovered": true and keeps its original account_id.

If the identity is already connected, the new-connect branch returns 409 ACCOUNT_ALREADY_LINKED. Pass that identity's account_id in the body on a follow-up POST /v1/auth/intent to re-authenticate it in place instead of creating a duplicate. When your workspace already owns that account, the error names your own account_id so you can reconnect it directly.

Every step above has a CLI equivalent under curviate account; see the CLI account reference.

Errors you may hit

CodeHTTPCauseFix
INVALID_REQUEST400email, password, or li_at sent at the top level instead of inside credentials / cookie; or user_agent missing on a cookie connect.Nest them as shown above.
UNAUTHORIZED401Missing or invalid API key. A cvt_test_ prefix lands here.Use your cvt_live_ key.
SEAT_NOT_FOUND404The seat_id does not exist or is not yours.Copy it from the seat table on the dashboard's Billing page.
PAYMENT_REQUIRED402No empty seat available.Use Add Seats on the dashboard's Billing page.
ACCOUNT_ALREADY_LINKED409That LinkedIn identity is already connected.Reconnect it by passing its account_id.
LINKEDIN_AUTH_FAILED401LinkedIn rejected the credentials.Check the email and password.
LINKEDIN_COOKIE_INVALID401The li_at cookie is expired or was captured under a different User-Agent.Re-export it, and send the matching user_agent.
CHECKPOINT_INVALID_CODE422Wrong verification code.Re-read the code, or re-request the notification.

Full envelope shapes are in the error reference.

Next steps

COMPANY · LEGAL

Privacy Policy

Redmer Holding GmbHLast updated August 4, 2026

Who we are

Curviate is operated by Redmer Holding GmbH ("Curviate", "we", "us"), a German GmbH registered at Amtsgericht Bonn, HRB 29957, registered address Hostertstraße 16, 53332 Bornheim, Germany. Full company details are on our Imprint. We haven't appointed a statutory Data Protection Officer, since our processing doesn't reach the scale or sensitivity that requires one. Privacy questions go to privacy@curviate.com.

The two roles we play

When you create an account and use Curviate, we process your own data (identity, billing, API keys, connector authorizations). For that data, we are the controller.

When you use Curviate to act on your own connected LinkedIn account, viewing profiles, sending messages, managing engagement, that content and those contacts belong to that account and its people. You are the controller of that data; we are the processor, acting only on your instructions, under a Data Processing Agreement available on request (see below). If one of your contacts has a question about being reached through Curviate, you're who they should contact first; email privacy@curviate.com if you need help routing it.

What we collect, and why

DataWhy
Account identity (name, email, sign-in method)Create and secure your account
Your LinkedIn credentialsOperate the actions you request
LinkedIn content returned by an API callFulfil that specific request, nothing more
API keys and connector (OAuth) authorizationsAuthenticate your API, CLI, MCP, or SDK requests
Billing detailsCharge you correctly and meet our tax obligations
Usage and security logsKeep the service reliable and abuse-free
Support messagesRespond to you
Website analytics, only if you opt inUnderstand how the site is used

We rely on our contract with you, our legitimate interest in running and securing the service, our legal obligations (tax law, for example), and, for analytics, your consent. We never sell your data or use it to train models.

Where it's processed, and who else touches it

Our infrastructure runs in the EU. Hosting: Railway. Database and auth: Supabase, Ireland. Email: Resend. Payments: Stripe. Network security: a DDoS-protection provider sits in front of our app and never sees or stores request content. LinkedIn connectivity: a third-party infrastructure provider that lets us execute LinkedIn actions on your behalf. Error tracking: Sentry, Frankfurt. Product analytics: PostHog, Frankfurt. Uptime monitoring: Better Stack.

We give the current, named list of every provider above to any customer who asks: security@curviate.com.

Data processing agreement

A data processing agreement under Article 28 of the GDPR is available to business customers on request. Email security@curviate.com and we will send you the current version.

Outside the EU

All customer LinkedIn data, account data, and telemetry are processed and stored exclusively in EU regions of our sub-processors. A few providers we rely on (Stripe and Sentry, for example) are headquartered outside the EU/EEA; where that applies, it's covered by their own GDPR safeguards, typically the EU Standard Contractual Clauses.

How long we keep it

DataRetention
Account and workspace dataWhile your account is active
Closed accountDeleted immediately and irreversibly; see Deleting your account below
LinkedIn credentialsUntil you disconnect that account
LinkedIn contentNot stored; any transient cache clears within 1 hour, never indexed, never used for training
API keysUntil you revoke or rotate them
Connector (OAuth) authorizationsAccess token ~1 hour; refresh token up to ~12 months, or until you revoke it, whichever comes first
Billing recordsAs required by German tax law, currently up to 10 years
LogsA short operational window; metadata only, never message content

The 12-month figure above is a server-side credential for a connected AI agent or app. It is not a cookie and doesn't touch your browser session; see Cookies below for that. You can see and revoke every connector from Authorized applications in your dashboard at any time.

Cookies

We keep cookies to a minimum, and ask before anything beyond the essentials runs.

Strictly necessary, no consent needed:

NamePurposeExpiry
cc_cookieRemembers your cookie choice12 months
curviate-themeRemembers light/dark mode (local storage, not a cookie)Persistent
sb-*-auth-tokenKeeps you signed inWhile active; cleared on sign-out

Analytics, only if you accept:

NamePurposeExpiry
_gaGoogle Analytics: distinguishes visitors2 years
_gidGoogle Analytics: distinguishes visitors24 hours
_ga_<container id>Google Analytics: persists session state2 years

No advertising cookies, ever. Accept and reject are equally easy, and you can change your mind any time via Cookie Preferences in the footer; we won't ask again for 12 months unless something material changes. Our LinkedIn connect flow and OAuth authorization screen never set anything beyond the essentials, so no banner appears there.

Connecting an AI agent or app

Curviate is built for AI agents and automated clients as much as for people. If you connect an app like Claude, or your own code, via an API key or an OAuth connector, it can act on your workspace within the access you gave it. What it does with anything it receives back, including what it sends to its own AI model, is between you and that provider; review its practices before connecting it. Review and revoke any connection any time from your dashboard.

Deleting your account

You can delete your account yourself, from Settings in your dashboard. It takes effect immediately and it cannot be undone. There is no grace period and nothing to restore afterwards, so export anything you want to keep before you start.

Deleting removes your sign-in identity, which frees your email address for reuse straight away, along with your profile, your workspace membership and settings, your API keys, and your seats. For any connected LinkedIn account, we instruct our infrastructure provider to delete it, and your access ends immediately. Records of the connection itself can remain in our systems; email privacy@curviate.com if you need those removed as well. LinkedIn content was never stored in the first place, so there is none of it to delete.

A few things are kept on purpose. We would rather name them than claim a clean sweep:

  • Billing records, for as long as German tax law requires. They hold plan, seat count, amount, and payment references; no name, no email, no LinkedIn data.
  • A record that the deletion happened, so we can show you or a regulator that we did it.
  • A security log of which requests were made, kept for 90 days and then removed automatically. It records that a request happened, never what was in it.
  • A one-way fingerprint, if you used a free trial, that lets us recognise a repeat trial. It holds no readable identifier and cannot be read back into your name, your email, or your LinkedIn profile.

Internal workspace identifiers can also remain in operational records such as queue entries and rate-limit counters. Those carry no name, no email, and no content. If you want to know exactly what is left for your own account, ask us at privacy@curviate.com.

Your rights

You can access, correct, delete, restrict, or object to your data, port it elsewhere, and withdraw consent at any time: email privacy@curviate.com. A copy of your data in a machine-readable format is available on request. We don't make automated decisions about you that have a legal or similarly significant effect. You can also complain to a supervisory authority; ours is the Landesbeauftragte für Datenschutz und Informationsfreiheit Nordrhein-Westfalen (LDI NRW), www.ldi.nrw.de, though you're free to complain to the one in your own country instead.

Keeping it secure

Credentials are encrypted and never logged, returned, or shared. LinkedIn actions run through native, humanized flows; full detail is on our Security & Compliance page. If a breach puts your rights at risk, we'll notify the authorities and you, as GDPR requires. Curviate isn't directed at, or offered to, anyone under 16.

Changes

We'll update this page when our practices change, and reset the cookie prompt if the change is material.

Contact