---
title: "Read workspace onboarding assumptions and settings"
method: GET
path: "/v1/workspace"
tags: ["Workspace"]
---

# Read workspace onboarding assumptions and settings

`GET /v1/workspace`

## Request

- Base URL: `https://api.trayo.ai`
- URL: `https://api.trayo.ai/v1/workspace`
- Auth: HTTP bearer

**Required permissions:** `accounts:read`.

The workspace your key acts in, including generated solution cards, buyer personas, the ICP buyer profile, and their prose projections. Review generated assumptions with the user before acting on them. One workspace per key; there is nothing to select.

**The stakeholder definition is what makes stakeholder search yours.** Two stages read it. The account-level search that runs when a company is added to the workspace turns it into search keywords and selects people at the company against it; without a definition that search seeds on seniority alone (chiefs, VPs, directors of any function). The per-event stage that a discovery run started with `people: "add"` runs attaches people, with a per-person `reasoning`, to each event; without a definition that stage does not run at all and `expand=people` is `[]` on every event.

**Set it FIRST — before you add accounts, and before you run discovery.** It steers work that starts after it is set and is never applied retroactively, and the account-level search for a company starts the moment that company is added. An account added before the definition existed keeps the people that search found for it then; the way to search it against the new definition is `POST /v1/stakeholders/search`.

## Response `200`

- WorkspaceDto
  - `id` string, uuid, required: The workspace your key acts in.
  - `name` string, required: The workspace name, as the Trayo app shows it. Read-only here.
  - `revision` string, required: Opaque version of the saved onboarding settings. Pass it as expectedRevision when replacing solution or persona lists; re-read on conflict.
  - `stakeholderCriteria` string, nullable, required: Your stakeholder definition, in plain prose: who buys, owns, or influences what you sell, and who does not. It steers BOTH stakeholder stages — the account-level search that runs when a company is added to the workspace, and the per-event people (with `reasoning`) a discovery run started with `people: "add"` attaches. Null until set: the account-level search then seeds on seniority alone and no people are attached to events.
  - `solutions` string, nullable, required: What you sell, in prose. Read by the reasoning that explains why a person or event matters, and by the app when it drafts a stakeholder definition. Null until set.
  - `solutionsBrief` string, nullable, required: A one- or two-sentence version of `solutions`. Null until set.
  - `solutionItems` object[], nullable, required: Editable solution cards saved during onboarding. Null means this workspace only has prose; an empty array means no cards.
    - `name` string, required
    - `description` string, required
    - `url` string, nullable, required
    - `icon` string, nullable, required
  - `stakeholderPersonas` object[], nullable, required: Editable buyer and stakeholder personas saved during onboarding. Null means only prose is available.
    - `title` string, required
    - `seniority` 'C-level' | 'VP' | 'Director' | 'Manager', required
    - `buyingRole` 'economic_buyer' | 'decision_maker' | 'champion' | 'technical_evaluator', nullable, required
    - `rationale` string, required
    - `icon` string, nullable, required
  - `buyerProfile` object, required: The ICP assumptions: buyer thesis, industries, size and geography, exclusions and search angles. Always present. This field was previously null when no profile was saved; it no longer is, so read `buyerProfileSource` instead of checking for null, and check it before treating this as fact.
    - `isB2b` boolean, required
    - `notB2bReason` string, required
    - `sellerMode` 'horizontal' | 'vertical' | 'local_smb' | 'smb_horizontal' | 'enterprise', required
    - `modeReason` string, required
    - `geography` object, required
      - `scope` 'local' | 'regional' | 'national' | 'global', required
      - `countries` string[], required
      - `regionLabel` string, required
      - `states` string[], required
      - `cities` string[], required
      - `radiusNote` string, required
    - `sizeBand` object, required
      - `min` integer, required
      - `max` integer, required
    - `sameIndustryBuyer` boolean, required
    - `rivalDefinition` string, required
    - `buyerThesis` string, required
    - `positiveSignals` string[], required
    - `industries` string[], required
    - `technologies` string[], required
    - `competitorTerms` string[], required
    - `categoryKeywords` string[], required
    - `existingCustomers` string[], required
    - `generationAngles` string[], required
  - `buyerProfileSource` 'saved' | 'partial' | 'inferred' | 'unreadable', required: `saved`: read from onboarding's stored ICP. `partial`: the fields sent to this API are the caller's own; every field never sent is still the placeholder below. `inferred`: nothing was saved, so you got a fixed placeholder — the same wide defaults for every workspace, not read from your site or your name. `unreadable`: an ICP is saved but not in a shape this API can read, so you got that same placeholder and patching is refused; edit it in the Trayo app. Only `saved` fields and fields you supplied yourself are facts about this workspace; confirm the rest before relying on them.
  - `brand` object, nullable, required: Your company brand, from your workspace website: name, description, colors, social profiles, and logo images you can embed. Null when no brand is known for the website.
    - `domain` string, required: The website domain the brand belongs to, e.g. "stripe.com".
    - `name` string, nullable, required: The brand name.
    - `description` string, nullable, required: A one- or two-sentence description of the company.
    - `colors` string[], required: Brand colors as lowercase `#rrggbb`, most prominent first.
    - `socials` object[], required: Social profiles found for the brand.
      - `network` string, required: Network key, e.g. `linkedin`, `twitter`, `github`.
      - `url` string, required: Profile URL.
    - `logoUrl` string, required: Square logo image URL. Public, no key needed.
    - `brandmarkUrl` string, nullable, required: Wide logo lockup image URL (800px wide PNG). Public, no key needed. Null when none.
    - `bannerUrl` string, nullable, required: Social banner image URL (800px wide). Public, no key needed. Null when none.

## Other responses

- `401`: Missing, malformed, or incorrect bearer token. Every way authentication can fail returns the identical body, so do not try to infer which one it was, and do not repeat the call with the same key. `unauthorized` (`retry: do_not_retry`) — Missing, malformed, or incorrect bearer token. Identical body for every cause.
- `403`: The key grant and its owner's current workspace role must both include every scope required by the operation. `insufficient_scope` (`retry: do_not_retry`) — Scope denial details distinguish missing_key_scope (use a key granting every missing scope), owner_permission_limit (the owner needs the missing workspace permissions), and invalid_key_scope (replace the key with supported scopes). Membership and undeclared-route denials remain generic.
- `429`: Every route is rate limited per workspace across REST, MCP, all keys and OAuth users, `GET /v1/whoami` included, so a balance poll competes with the work it is watching. For `rate_limited`, wait `Retry-After` seconds (also `details.retryAfterSeconds`), then send the same request once; do not retry sooner. `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` describe the workspace's bucket, and they come back on successful responses too: read them and slow down BEFORE you are refused. A burst from one network can also be refused before the key is checked; that 429 carries `Retry-After` and no `RateLimit-*` headers. `rate_limited` (`retry: retry_later`) — The workspace or source network exhausted its request budget for one bucket. `details.retryAfterSeconds` (and `Retry-After` on REST) is the wait: wait that long, then retry the same request once. Do not poll or retry sooner; an early retry is refused again. If you cannot wait, stop and tell the user when to retry; do not fill the wait with other calls. `details.limit` and `details.bucket` name the exhausted budget. When present, RateLimit-* headers describe the active budget and reset.
- `500`: Something failed on our side. Nothing about the request needs changing. `internal_error` (`retry: retry_later`) — Something failed on our side. `message` never describes it — quote `requestId` when reporting. Retry with exponential backoff and GIVE UP after a few attempts: `retry_later` here does not mean the failure is known to be transient, only that nothing about your request needs changing. A request that fails this way repeatedly will not start working.

---

[API](https://skmtc.dev/trayo/apis/trayo-api.md) · [All operations](https://skmtc.dev/trayo/apis/trayo-api/llms.txt) · [OpenAPI document](https://skmtc.dev/trayo/apis/trayo-api/revisions/0af452dce6de?raw)
