Ephemeral alpha instance — all state will be lost after 31 Aug 2026

← dashboard

Integrating "Login with mycorra"

This guide is for third-party app developers who want their users to sign in with mycorra and run LLM inference against the API keys those users have stored in their mycorra account. Your app never sees the raw provider keys — mycorra holds them and proxies inference on the user's behalf.

mycorra implements the standard OAuth 2.0 authorization-code grant (RFC 6749, §4.1). If you've integrated "Sign in with GitHub/Google", this is the same shape.

deployment (locally, http://localhost:8080).

access tokens live 1 hour.

At a glance

1. Once: register your app → get client_id + client_secret (§1). 2. Per user: send them to /oauth/authorize → they consent → you get a code (§2). 3. Per user: exchange code at /oauth/tokenaccess_token (§3). 4. Per request: GET /api/providers to see what they configured (§4), then POST /api/inference/{provider} with the provider's native body (§5).

The access token is the only credential you need for inference. Keep it server-side; refresh by re-running steps 2–3 when it expires (1 h) or a call returns 401.


1. Register your app (one time)

While logged in to mycorra, register an OAuth client. The redirect_uris field is newline-separated; every value must be an exact URL your app will send as redirect_uri later (no wildcards, no path-prefix matching).

curl -sX POST https://mycorra.example/clients \
  -b cookies.txt \                       # a mycorra session cookie
  --data-urlencode 'name=My Cool App' \
  --data-urlencode $'redirect_uris=https://mycoolapp.com/auth/callback'

Response — the secret is shown exactly once, store it securely:

{
  "client_id": "b1c2...",
  "client_secret": "9f8e...",
  "redirect_uris": ["https://mycoolapp.com/auth/callback"]
}

2. Send the user to authorize

Redirect the user's browser to /oauth/authorize:

GET https://mycorra.example/oauth/authorize
      ?response_type=code
      &client_id=b1c2...
      &redirect_uri=https://mycoolapp.com/auth/callback
      &state=<opaque-random-anti-CSRF-value>
ParamRequiredNotes
response_typeyesmust be code
client_idyesfrom step 1
redirect_uriyesmust exactly match a registered URI
statestrongly recommendedopaque value you generate; verify it on return to prevent CSRF
https://mycoolapp.com/auth/callback?code=<authorization_code>&state=<the-same-state>

Verify state matches what you sent before continuing.


3. Exchange the code for an access token

Server-to-server call (never from the browser — it uses your client secret). Client credentials may go in the body or via HTTP Basic auth.

curl -sX POST https://mycorra.example/oauth/token \
  -d grant_type=authorization_code \
  -d code=<authorization_code> \
  -d redirect_uri=https://mycoolapp.com/auth/callback \
  -d client_id=b1c2... \
  -d client_secret=9f8e...

Response:

{
  "access_token": "eyJ...hmac",
  "token_type": "Bearer",
  "expires_in": 3600
}

The code is single-use — a second exchange returns 400 {"error":"invalid_grant"}. Other error codes follow OAuth2: invalid_client, unsupported_grant_type.


4. Discover which providers the user configured

Before offering a provider, ask which ones the signed-in user actually has a key for — so you don't present options that will fail:

curl -s https://mycorra.example/api/providers \
  -H "Authorization: Bearer eyJ...hmac"
{ "providers": [ { "id": "openai", "name": "OpenAI" }, { "id": "mistral", "name": "Mistral" } ] }

Only providers with a stored key are returned. An empty list means the user should add a key at /dashboard first.

5. Call inference with the access token

POST https://mycorra.example/api/inference/{provider}
Authorization: Bearer <access_token>
Content-Type: application/json

<the provider's own native request body>

{provider} is one of openai, anthropic, openrouter, mistral. mycorra does not translate schemas — you send the provider's native request body and get its native response back. mycorra only injects the user's stored key, forwards the call, meters token usage, and relays the result.

Request body per provider

OpenAI-compatible providers (openai, openrouter, mistral) — the /chat/completions schema:

{
  "model": "gpt-4o-mini",
  "messages": [{"role": "user", "content": "Say hello in French."}]
}

Anthropic — the /messages schema (note the required max_tokens):

{
  "model": "claude-3-5-haiku-latest",
  "max_tokens": 1024,
  "messages": [{"role": "user", "content": "Say hello in French."}]
}

You may set any field the upstream provider supports (temperature, tools, etc.) — it is passed through untouched.

Example

curl -sX POST https://mycorra.example/api/inference/openai \
  -H "Authorization: Bearer eyJ...hmac" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'

Response

On 200 the body is the provider's response verbatim. For OpenAI-compatible providers that's:

{
  "id": "chatcmpl-...",
  "choices": [{"message": {"role": "assistant", "content": "Bonjour !"}}],
  "usage": {"prompt_tokens": 9, "completion_tokens": 3, "total_tokens": 12}
}

Anthropic returns {"content": [{"type": "text", "text": "Bonjour !"}], "usage": {"input_tokens": 9, "output_tokens": 3}}. Parse whichever shape matches the provider you called.

Errors

StatusMeaningWhat to do
200upstream response, relayed as-is
401missing / invalid / expired access token, or the user revoked your appre-run the authorization flow (steps 2–3)
404unknown {provider}use a supported provider id
412the user has no key stored for this providerprompt them to add one, or offer a provider from /api/providers
502mycorra couldn't reach the upstream providerretry with backoff
otherany non-2xx from the upstream (e.g. 402 insufficient credits, 429 rate limit) is relayed as-issurface the provider's error body to the user

Because non-2xx upstream responses are passed through, always be ready to read an error body shaped like the provider's own error (not mycorra's).

Notes

the full response to meter usage. Requests complete in a single JSON body.

tokens out, estimated cost) and visible to the user on their mycorra usage page. Your app doesn't need to report anything.


Security notes

Register every callback you use.

a mycorra session cookie can't be replayed as an API token and vice versa.

keep a secret (SPA/native), treat this as a confidential-client-only integration and proxy the token exchange through your backend.

performs inference. You cannot read a user's provider key through any endpoint.

can end your app's session; outstanding access tokens then return 401. Treat a sudden 401 as "re-run the authorization flow", not a hard error.


Endpoint reference

MethodPathAuthPurpose
POST/clientsmycorra sessionregister an OAuth client
GET/oauth/authorizeuser sessionconsent screen, issues a code
POST/oauth/tokenclient secretexchange code → access token
GET/api/providersBearer accesslist the user's configured providers
POST/api/inference/{provider}Bearer accessproxy inference with user's key

A minimal, self-contained example of the whole flow lives in the end-to-end test: internal/web/web_test.goTestOAuthLoginWithMycorraEndToEnd.