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 this page (and the schema file, for full field-level detail) is the source of truth.

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.

Operations

The full catalog of 244 API-token-accessible operations, grouped by area. Names are intentionally close to self-describing; look up full argument and return shapes via the schema file, or ask your eccuity contact.

Identity

requires identity

Minimal profile info for “Sign in with eccuity”.

OperationTypeScopeNotes
fetchCurrentUserQueryidentity

Profile & Account

requires profile.read / profile.write

KYC status and submission, investor preferences, AI assistant threads, passkeys and device tokens, appointments, likes, newsletter status, broker-credential linking, and general account settings.

OperationTypeScopeNotes
createOAuthLinkMutationprofile.write
createStrategyAssetVotesMutationprofile.write
deleteAssistantThreadMutationprofile.writeDelete an assistant thread.
deleteBrokerCredentialMutationprofile.write
deleteOAuthLinkMutationprofile.write
generateLikeableAssetsMutationprofile.write
insertMessageIntoAssistantThreadMutationprofile.writeInsert a message into an existing assistant thread.
registerDeviceTokenMutationprofile.write
removeDeviceTokenMutationprofile.write
removePasskeyMutationprofile.write
saveBrokerCredentialMutationprofile.write
saveInvestorPreferencesMutationprofile.write
startAssistantThreadWithMessageMutationprofile.writeStarts a new assistant thread with an initial message. Checks your monthly thread quota first.
submitKycMutationprofile.write
toggleLikeMutationprofile.write
unregisterDeviceTokenMutationprofile.write
updateAntiPhishingCodeMutationprofile.write
updateAssistantThreadMutationprofile.writeUpdate an assistant thread (e.g. rename, toggle like).
updateChartStartAtZeroMutationprofile.write
updateEccuityInvestAccountConfigMutationprofile.write
updateMfaMutationprofile.write
updateMfaQrCodeMutationprofile.write
updateNetWorthAsHomePageMutationprofile.write
updateOnfidoOutcomeMutationprofile.write
updateUserAccountMutationprofile.write
updateUserKycAgreementsMutationprofile.write
updateUserKycCompanySetupMutationprofile.write
updateUserKycContactInformationMutationprofile.write
updateUserKycDisclosuresMutationprofile.write
updateUserKycFileDocumentsMutationprofile.write
updateUserKycIdentityInformationMutationprofile.write
updateUserKycTrustedContactMutationprofile.write
updateUserKycW8BENFormDocumentMutationprofile.write
updateUserProfileMutationprofile.write
fetchAssistantThreadByIdQueryprofile.read
fetchEccuityInvestAccountActivitiesQueryprofile.read
fetchEccuityInvestAccountConfigQueryprofile.read
fetchEccuityInvestAccountDocumentByIdQueryprofile.read
fetchEccuityInvestAccountDocumentsQueryprofile.read
fetchInvestorPreferencesQueryprofile.read
fetchKYCQueryprofile.read
fetchMyAssistantThreadsQueryprofile.readFetch all assistant threads owned by the current user.
fetchMyDeviceTokensQueryprofile.read
fetchMyPasskeysQueryprofile.read
fetchUserAppointmentsQueryprofile.read
fetchUserLikesQueryprofile.read
getNewsletterStatusQueryprofile.read
getOnfidoTokenQueryprofile.read
assistantNotificationsSubscriptionprofile.readLong-lived subscription for AI assistant proactive notifications, filtered to the current user.
assistantStreamSubscriptionprofile.readStreams the AI assistant's reply to a message in real time, token by token.

Portfolio & Net Worth

requires portfolio.read / portfolio.write

Everything under the Net Wealth area of the app (assets, incomes, expenses, liabilities, goals, household, documents, share links), plus investing strategies, trade ideas, broker account/position/order reads, custom instruments, and portfolio history.

OperationTypeScopeNotes
addInstrumentPriceMutationportfolio.write
addWalletWatchMutationportfolio.write
confirmNetWorthDividendMutationportfolio.writeConfirm a single auto-imported, still-pending received dividend.
confirmNetWorthTradeMutationportfolio.writeConfirm a single auto-imported, still-pending split or consolidation trade.
createCryptoWalletMutationportfolio.write
createCustomInstrumentMutationportfolio.write
createCustomInstrumentWithDetailsMutationportfolio.write
createNetWorthDividendMutationportfolio.writeRecord a dividend received for an asset, for custom instruments the automated importer doesn't cover.
createNetWorthDocumentUploadMutationportfolio.write
createNetWorthExpenseMutationportfolio.write
createNetWorthGoalMutationportfolio.write
createNetWorthIncomeMutationportfolio.write
createNetWorthLiabilityMutationportfolio.write
createNetWorthLiabilityTradeMutationportfolio.write
createNetWorthShareLinkMutationportfolio.write
createNetworthPortfolioMutationportfolio.write
createStrategyMutationportfolio.write
deleteCustomInstrumentMutationportfolio.write
deleteInstrumentFromPortfolioMutationportfolio.write
deleteInstrumentPriceMutationportfolio.write
deleteNetWorthDocumentMutationportfolio.write
deleteNetWorthExpenseMutationportfolio.write
deleteNetWorthFileMutationportfolio.write
deleteNetWorthGoalMutationportfolio.write
deleteNetWorthIncomeMutationportfolio.write
deleteNetWorthLiabilityMutationportfolio.write
deleteStrategyMutationportfolio.write
deleteWalletWatchMutationportfolio.write
deleteWealthRelayPortfolioMutationportfolio.write
finalizeNetWorthDocumentMutationportfolio.write
generateNetWorthReportMutationportfolio.read
renameNetWorthPortfolioMutationportfolio.write
retryNetWorthFileExtractionMutationportfolio.write
retryTradeEmailMutationportfolio.write
revokeNetWorthShareLinkMutationportfolio.write
setCustomInstrumentMetaDataMutationportfolio.write
setPortfolioEmailSettingsMutationportfolio.write
updateCustomInstrumentMutationportfolio.write
updateInstrumentPriceMutationportfolio.write
updateNetWorthDisplayCurrencyMutationportfolio.write
updateNetWorthExpenseMutationportfolio.write
updateNetWorthGoalMutationportfolio.write
updateNetWorthIncomeMutationportfolio.write
updateNetWorthLiabilityMutationportfolio.write
updateStrategyMutationportfolio.write
uploadNetWorthFileMutationportfolio.write
fetchActiveStrategyQueryportfolio.read
fetchAllActiveStrategiesQueryportfolio.read
fetchDiscoverListsQueryportfolio.read
fetchEccuityInvestAccountBalancesQueryportfolio.read
getAllTradeIdeasQueryportfolio.read
getAvailableSharesToAttachQueryportfolio.read
getBrokerAccountsQueryportfolio.read
getClosedTradeIdeasQueryportfolio.read
getDefaultLiabilityPortfolioQueryportfolio.read
getDefaultPortfolioQueryportfolio.read
getDepositsAndWithdrawalsChartQueryportfolio.read
getInstrumentPricesQueryportfolio.read
getOrdersQueryportfolio.read
getPortfolioEmailsQueryportfolio.read
getPortfolioHistoryQueryportfolio.read
getPortfoliosQueryportfolio.read
getPositionsQueryportfolio.read
getRebalancePreviewQueryportfolio.read
getTradeIdeaQueryportfolio.read
getTradeIdeasQueryportfolio.read
getWalletWatchesQueryportfolio.read
getWealthRelayTradesQueryportfolio.read
netWorthAllPortfolioFilesQueryportfolio.readFiles across the assets portfolio and all of its sub-portfolios, newest first.
netWorthAssetDividendsQueryportfolio.readReceived dividends for a single instrument (symbol) in your portfolio.
netWorthAssetHistoryQueryportfolio.read
netWorthAssetsQueryportfolio.read
netWorthDashboardQueryportfolio.read
netWorthDisplayCurrencyQueryportfolio.read
netWorthDocumentsQueryportfolio.read
netWorthExpenseHistoryQueryportfolio.read
netWorthExpensesQueryportfolio.read
netWorthGoalsQueryportfolio.read
netWorthHouseholdQueryportfolio.read
netWorthIncomeHistoryQueryportfolio.read
netWorthIncomesQueryportfolio.read
netWorthLiabilitiesQueryportfolio.read
netWorthLiabilityHistoryQueryportfolio.read
netWorthPortfolioFilesQueryportfolio.read
netWorthShareLinksQueryportfolio.read
searchInstrumentsQueryportfolio.read

Trading

requires trade

Placing, editing, and cancelling orders; managing and rebalancing active strategies; paper-trading accounts.

OperationTypeScopeNotes
cancelOrderMutationtrade
contributeToActiveStrategyMutationtrade
createActiveStrategyMutationtrade
createGembrokerPaperAccountMutationtrade
createTradeMutationtrade
createTradeForAssetMutationtrade
createWealthRelayTradeMutationtrade
deleteWealthRelayTradeMutationtrade
editOrderMutationtrade
fundPaperAccountMutationtrade
liquidateActiveStrategyMutationtrade
liquidatePositionMutationtrade
placeOrderMutationtrade
rebalanceActiveStrategyMutationtrade
setActiveStrategyStatusMutationtrade
setAssetSharesMutationtrade
subscribeToStrategyMutationtrade
syncWithSourceStrategyMutationtrade
unsubscribeFromStrategyMutationtrade
updateActiveStrategyMutationtrade
updateWealthRelayTradeMutationtrade

Funding & Payments

requires funding.read / funding.write

Deposits, withdrawals, saved bank accounts and cards, crypto wallets and auto-sweeps, FX rates and quotes, billing and subscriptions, data exports.

OperationTypeScopeNotes
cancelSubscriptionMutationfunding.write
createCryptoAutoSweepMutationfunding.write
createCryptoCurrencyWalletsMutationfunding.write
createPendingUserTransactionMutationfunding.write
createWithdrawalRequestMutationfunding.write
deleteCardMutationfunding.write
deleteCryptoAutoSweepMutationfunding.write
deleteExpiredPendingUserTransactionsMutationfunding.write
deleteSavedBankAccountMutationfunding.writeRemoves your saved payout bank account.
deleteSavedPayoutAccountMutationfunding.write
deleteWhitelistedWalletMutationfunding.write
markAllDepositsAsViewedMutationfunding.write
markDepositAsViewedMutationfunding.write
markExportJobsViewedMutationfunding.read
optInInterestMutationfunding.write
requestCryptoWithdrawalMutationfunding.write
requestExportMutationfunding.read
requestWhitelistWalletMutationfunding.write
saveBankAccountNZDMutationfunding.write
saveBankAccountUSDMutationfunding.write
saveNewCardMutationfunding.write
savePayoutBankAccountNZDMutationfunding.write
savePayoutBankAccountUSDMutationfunding.write
signAgreementsMutationfunding.write
signCustomerAgreementMutationfunding.write
simulatePaperDepositMutationfunding.write
subscribeUserMutationfunding.write
updateWhitelistedWalletTravelRuleMutationfunding.write
fetchAccountAgreementsQueryfunding.read
fetchCryptoCurrencyTransfersQueryfunding.read
fetchCryptoCurrencyWalletsQueryfunding.read
fetchCryptoWalletAddressQueryfunding.read
fetchCurrentRatesQueryfunding.read
fetchDefaultPaymentProviderQueryfunding.read
fetchPendingUserTransactionsQueryfunding.read
fetchSavedBankAccountQueryfunding.read
fetchSavedPayoutAccountQueryfunding.read
fetchUserDepositsQueryfunding.read
fetchWhitelistedWalletsQueryfunding.read
getBankDetailQueryfunding.read
getCryptoAutoSweepsQueryfunding.read
getMyForexFeeRateQueryfunding.readReturns the FX fee rate you'd pay on a deposit or withdrawal (0.005 = 0.5%).
getTemporaryQuoteQueryfunding.read
getUSDBankingDetailsQueryfunding.read
listCardsQueryfunding.read
listUserSubscriptionsQueryfunding.read
myExportJobsQueryfunding.read
searchVASPsQueryfunding.read
exportJobUpdatedSubscriptionfunding.read

Public / Market Data

requires public

Requires no scope beyond a valid token (the public scope, any authenticated token can call these; a handful, like register, don't even require a token). Asset lookup, market hours, news, option chains, historical prices, strategy browsing, and account-recovery/registration flows.

OperationTypeScopeNotes
emailLinkToInvestmentCalculatorMutationpublic
recordPushReceiptMutationpublicRecords delivery of a push notification. No auth required, so it can be called from service workers.
registerMutationpublic
requestMfaEmailMutationpublic
requestPasswordResetMutationpublic
resendVerificationEmailMutationpublic
resetPasswordMutationpublic
subscribeToNewsletterMutationpublic
verifyEmailMutationpublic
fetchAnnouncementQuerypublic
fetchAssetByAssetIdQuerypublic
fetchAssetByTickerQuerypublic
fetchAssetPerformanceQuerypublic
fetchAssetsByAssetIdsQuerypublic
fetchAssetsByTickersQuerypublic
fetchAvailableRatesQuerypublic
fetchExpertsQuerypublic
fetchMarketHoursQuerypublic
fetchNewsByAssetIdsQuerypublic
fetchStrategiesQuerypublic
fetchStrategyQuerypublic
getAvailableBrokerIntegrationsQuerypublic
getAvailableCountriesQuerypublic
getCurrentStockPriceQuerypublic
getExpirationDatesQuerypublic
getOptionChainQuerypublic
getOptionHistoricalDataQuerypublic
getStockHistoricalDataQuerypublic
getStrikesQuerypublic
getTradableAssetsQuerypublic
healthCheckQuerypublic
listSubscriptionsQuerypublic
searchBarQuerypublic
sharedNetWorthAssetHistoryQuerypublic
sharedNetWorthDashboardQuerypublic
livePriceDataSubscriptionpublic
newsUpdatesSubscriptionpublic

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.