Pricing

For developers

The eccuity API

A GraphQL API covering almost everything the eccuity app can do: net worth and portfolio data, trading, funding, and more. Secured with OAuth 2.0, so you, or an app you build, only ever get access to what you explicitly authorize.

Get API credentials

Requires a paid eccuity plan (Plus, Pro, or Private).

Overview

The API is available to anyone on a paid eccuity plan who wants to connect an external tool to their account: a personal script, a spreadsheet, an AI assistant, or a full third-party app that other eccuity users could also connect to. Free accounts can authenticate for basic sign-in purposes only, but can't grant a token access to portfolio, trading, or funding data.

Every operation requires a specific scope, and a token can only do what it was explicitly granted, regardless of what your account can otherwise do. GraphQL introspection is disabled in production, so the full reference below, generated from the live schema, is the source of truth for argument and return shapes.

Use cases

A sample of what people build on top of the API, from inside eccuity and out.

Built at eccuity

Trackers, run by agents

eccuity's own agents platform calls the API to run Trackers (model portfolios) autonomously: rebalancing, contributions and reporting, without anyone clicking through the app.

Built at eccuity

A custom trading strategy

One of our traders built a standalone trading application on top of the API, running a systematic strategy end to end: reading positions, placing and managing orders.

What you could build

Your own net worth dashboard

Pull your net worth, holdings and performance into a personal spreadsheet, a Notion database, or a command-line report on your own schedule.

What you could build

An AI assistant that knows your portfolio

Connect an MCP-compatible AI assistant to your account so it can answer questions about your holdings, or place a trade when you ask it to, within the scopes you grant.

What you could build

An app your clients connect to

Build a third-party app, budgeting tool, or portfolio tracker that other eccuity users log into and authorize with their own account, via the authorization code + PKCE flow.

What you could build

Adviser tooling across a book of clients

A lightweight dashboard pulling net worth and portfolio data across every client account your practice has been authorized to see.

Quick start

The fastest way to make your first authenticated call, no redirect flow, no consent screen, is a server-to-server (client credentials) credential. Right for a personal script or backend service acting on your own account.

  1. 1.Log into the eccuity app and go to Settings → API Credentials.
  2. 2.Click New credential. Give it a label. Leave “Public client (PKCE)” unchecked.
  3. 3.Copy the Client ID and Client Secret shown. The secret is shown once, and can't be retrieved again.
  4. 4.Exchange them for an access token. Your exact API base URL is shown on the Settings → API Credentials page.
    bash
    curl -X POST https://api.<your-eccuity-domain>/oauth/token \
      -u "ecc_id_xxxxxxxx:ecc_sk_xxxxxxxx" \
      -d "grant_type=client_credentials" \
      -d "scope=full_access"

    Response:

    json
    {
      "access_token": "ecc_at_xxxxxxxxxxxxxxxx",
      "token_type": "Bearer",
      "expires_in": 3600,
      "scope": "identity profile.read profile.write portfolio.read portfolio.write trade funding.read funding.write"
    }
  5. 5. Call the API with the token, a Bearer token good for one hour.
    bash
    curl -X POST https://api.<your-eccuity-domain>/graphql \
      -H "Authorization: Bearer ecc_at_xxxxxxxxxxxxxxxx" \
      -H "Content-Type: application/json" \
      -d '{"query": "query { fetchCurrentUser { id name email } }"}'

Authentication

eccuity's API is a standard OAuth 2.0 authorization server, with two flows depending on what you're building. Both end the same way: a Bearer access token sent as Authorization: Bearer <token> on every request.

Client credentials

A personal script or backend acting on your own account. Confidential credential (client ID + secret), no user interaction: trade the secret for a token directly. Created via Settings → API Credentials.

Authorization code + PKCE

A third-party app that other eccuity users will also log into and authorize. Public credential (client ID only), the user logs in and clicks Allow on a consent screen. Created via Settings → API Credentials (checking “Public client (PKCE)”), or self-registered via /oauth/register.

Option A: client credentials (server-to-server)

If you omit scope, your token only gets the identity scope. Always pass the scopes (or the full_access shortcut) you actually need. A confidential credential's token acts on your own account: there's no separate resource-owner step.

POST /oauth/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=full_access

Option B: authorization code + PKCE (OAuth apps)

Use this if other eccuity users will connect your app to their own account, the standard third-party-app flow.

  1. 1. Register a client: create a public credential yourself (Settings → API Credentials, with your redirect URI), or self-register via POST /oauth/register (RFC 7591 Dynamic Client Registration, the zero-touch path used by MCP-style clients). This only issues public (PKCE) clients, and your redirect URI domain must be pre-approved by eccuity (localhost/loopback is allowed automatically outside production, for local development).
  2. 2. Send the user to authorize. PKCE is mandatory: generate a code_verifier, derive a code_challenge (S256), and hold onto the verifier.
    GET /oauth/authorize
      ?response_type=code
      &client_id=ecc_id_xxxxxxxx
      &redirect_uri=https://yourapp.com/callback
      &scope=portfolio.read+trade
      &state=<random-string>
      &code_challenge=<S256-challenge>
      &code_challenge_method=S256

    A free-plan user can only ever grant identity: broader scopes requested from a free user are silently dropped down to what their plan allows, and the consent screen explains what was dropped.

  3. 3. Handle the redirect back to your redirect_uri with ?code=...&state=... (or ?error=access_denied if declined).
  4. 4. Exchange the code for a token.
    POST /oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code
    &code=<the code>
    &redirect_uri=https://yourapp.com/callback
    &client_id=ecc_id_xxxxxxxx
    &code_verifier=<the original verifier>

Redirect URIs must be https:// (plain http:// is only accepted for localhost/127.0.0.1, for local development) and can't contain a fragment.

Token lifetime

Access tokens last one hour and cannot be refreshed: there is no refresh_token grant. Get a new one when a token expires (trivial for client-credentials; a fresh consent round-trip for authorization-code apps). Deleting a credential in Settings immediately revokes every token and unredeemed authorization code issued from it.

Discovery endpoints

Standard OAuth/OIDC discovery is supported, so most OAuth libraries and MCP clients can configure themselves automatically.

  • GET /.well-known/oauth-authorization-server (RFC 8414)
  • GET /.well-known/oauth-protected-resource (RFC 9728)

Scopes

Every operation requires a specific scope. Your token can only call an operation if it was granted that scope, or a composite scope that includes it: a token can do no more than you can do yourself, but you can hand out a token that does less. Space-separate multiple scopes (e.g. scope=portfolio.read trade). Unknown scope names are rejected outright.

ScopeGrants
identityMinimal profile info, enough for “Sign in with eccuity” (fetchCurrentUser).
profile.readRead KYC status, investor preferences, AI assistant threads, passkeys, device tokens, appointments, and more.
profile.writeUpdate account and profile settings, KYC submissions, MFA, passkeys, AI assistant threads, broker credentials.
portfolio.readRead holdings, balances, performance, net worth data, strategies, trade ideas.
portfolio.writeNon-trading portfolio changes: net worth planning (income, expenses, goals, liabilities), custom instruments, tracked portfolios, share links.
tradePlace, edit, and cancel orders; manage strategies. One scope, not split per action.
funding.readRead deposits, withdrawals, cards, bank details, crypto wallets, billing and subscriptions.
funding.writeDeposits, withdrawals, saved payment and bank details, cards, crypto wallets, billing.
publicNo scope beyond a valid token needed: asset lookup, market hours, news, option chains, historical prices, strategy browsing, and account registration/recovery. A handful, like register, don't even require a token.

Composite (shortcut) scopes

ScopeExpands to
full_accessEvery leaf scope above except admin.
profileprofile.read + profile.write
portfolioportfolio.read + portfolio.write
fundingfunding.read + funding.write

A few operations exist only for the eccuity app itself and are never reachable with a Bearer token, regardless of scope: managing your own API credentials, and the OAuth consent screen's own internal fields. These return a FORBIDDEN error naming the field.

Full reference

Every operation, argument, and return type, generated straight from the live schema so it never drifts from what the API actually accepts.

eccuity API Reference

Full argument names and types, nested return shapes, and a live example query, variables, and response for every query, mutation, and subscription.

Open the reference ↗

A handful of operations are subscriptions: long-lived, pushed updates (live prices, news, export job status, AI assistant streaming) over a WebSocket connection rather than a plain POST. Use a GraphQL client that supports graphql-ws (e.g. Apollo Client, urql) and authenticate the socket with your Bearer token.

Example requests

Read your net worth dashboard (portfolio.read)

graphql
query {
  netWorthDashboard {
    totals {
      netWorth
      totalAssets
      totalLiabilities
      totalMonthlyExpenses
      totalAnnualIncome
    }
    assets { id }
    liabilities { id }
    goals { id }
    incomes { id }
    expenses { id }
  }
}

Place a market order (trade)

graphql
mutation {
  placeOrder(params: {
    assetId: "..."
    brokerCredentialId: "..."
    side: BUY
    type: MARKET
    quantity: 10
    notional: 0
    limitPrice: 0
    stopPrice: 0
    premarket: false
    timeInForce: DAY
  }) {
    id
    status
    quantityFilled
    averageFilledPrice
  }
}

MARKET orders still require limitPrice/stopPrice/notional in the input shape: pass 0 for whichever don't apply to your order type.

Check your current positions (portfolio.read)

graphql
query {
  getPositions(params: { tradingMode: "LIVE" }) {
    symbol
    quantity
    averageEntryPrice
    marketValue
    unrealizedProfitLoss
  }
}

Errors

Errors follow the standard GraphQL error shape:

json
{
  "errors": [
    {
      "message": "This token is missing the required scope \"trade\" for: placeOrder",
      "extensions": { "code": "FORBIDDEN" }
    }
  ]
}
SituationWhat you'll see
Missing or expired tokenA 401-equivalent GraphQL error, or the field resolves as unauthenticated.
Token lacks the required scopeFORBIDDEN, naming the field and the missing scope.
Calling a session-only field with a tokenFORBIDDEN: “This operation is not available to API tokens.”
Your account lacks the underlying permission (e.g. not a paid plan, not KYC'd)A descriptive validation error from the resolver itself.
Bad inputA standard GraphQL validation error before your query even executes.

Rate limits

  • 50 requests/second per authenticated user (or per IP if unauthenticated) across the API generally.
  • 5 requests/second on sensitive endpoints (login, token issuance, password reset, MFA): /oauth/token falls under this stricter limit.

Rate-limit headers (Rate-Limit-Remaining, Rate-Limit-Reset, Rate-Limit-Total) are returned on every response so you can back off proactively.

Good practices

  • Request only the scopes you need. A token is a bearer credential: anyone who obtains it can act as you, within its scopes. Don't request full_access for a script that only reads net worth data.
  • Treat the client secret like a password. It's shown once at creation time and never retrievable again. If you lose it, delete the credential and create a new one.
  • Expect 1-hour tokens. Build your integration to request a fresh token when a call fails with an auth error, rather than assuming long-lived access.
  • Delete unused credentials. Each one is a live door into your account; Settings → API Credentials shows last-used time and IP so you can spot anything that shouldn't still be active.
  • One credential per integration. Makes it easy to revoke a single integration without breaking everything else.

Credentials live in the app. This page is the reference.

eccuity can't issue API credentials from this site. Create and manage yours from inside your account.