{"version":"0.1.0","spec_url":"https://app.signal.geysera.com/signal-api/v1/openapi.json","auth":{"scheme":"Bearer","header":"Authorization","format":"sk_sig_...","obtain_at":"Signal dashboard -> Exports & API","note":"A key is scoped to one workspace. Results respect that workspace's plan caps, exactly as the dashboard does. Scopes are fixed when the key is created: to change reach, create a new key and revoke the old one.","scopes":{"read":"The /signal-api/v1 REST endpoints: accounts, visitors, attribution, recommendations.","copilot":"Ask questions in natural language at /signal-api/v1/copilot/ask. READ-ONLY: a key reaches the copilot's read tools and never the ones that change the workspace, whatever it is asked.","audiences":"/signal-api/v1/audiences: create, edit and delete audiences, review prospects, estimate runs and export accepted people (JSON, or CSV with ?format=csv). Spends nothing.","enrich":"Start a paid enrichment run (POST /signal-api/v1/audiences/{key}/runs) or change daily lookups. SPENDS enrichment credits, within the workspace's caps. Grant it only to a key that should spend."}},"rate_limit":{"requests":300,"window_seconds":60,"scope":"per key","exceeded_status":429},"conventions":{"pagination":{"params":"page (1-based), page_size (default 50, maximum 200)","response":"an array named for the resource (`accounts`, `visitors`) plus total, page, page_size. There is no `items` key.","note":"total is capped at what your plan may resolve, never the raw row count","out_of_range":"page_size above 200 is rejected with 422 rather than silently clamped, so a script cannot believe it read more than it did"},"error_shape":{"error_code":"stable machine-readable code; one of CLIENT_ERROR, CONFLICT, FORBIDDEN, GONE, INTERNAL, METHOD_NOT_ALLOWED, NOT_FOUND, PAYLOAD_TOO_LARGE, PAYMENT_REQUIRED, RATE_LIMITED, UNAUTHORIZED, VALIDATION_ERROR","message":"human-readable description","correlation_id":"echo of X-Correlation-ID; quote it in support requests","details":"optional structured context"},"error_codes":["CLIENT_ERROR","CONFLICT","FORBIDDEN","GONE","INTERNAL","METHOD_NOT_ALLOWED","NOT_FOUND","PAYLOAD_TOO_LARGE","PAYMENT_REQUIRED","RATE_LIMITED","UNAUTHORIZED","VALIDATION_ERROR"],"request_headers":{"Authorization":"Bearer sk_sig_... (required)","X-Correlation-ID":"optional; generated when absent, echoed on every response","X-Idempotency-Key":"optional; honoured on POST /audiences and POST /audiences/{key}/runs — a retry with the same key replays the first answer instead of creating or spending twice. The other audience writes are idempotent by design."},"response_headers":{"X-Correlation-ID":"the id for this request, generated when you do not send one","Retry-After":"seconds to wait, on 429 only. The real remaining window, not a fixed number — arrive late in a window and it is small"},"caps":"Accounts beyond your plan's monthly resolution cap are not returned, and `total` is capped to match, so a full page may not be the whole set. The response does not report how many exist beyond the cap.","key_limit":"A workspace may hold 10 active keys at once. Creating one beyond that returns 409; revoke an unused key first."},"router_prefixes":["/capabilities","/health","/signal","/signal-api","/version"],"copilot_tools":[{"name":"aov_decomposition","summary":"Splits AOV into average unit price x average units per order, before vs after a boundary date."},{"name":"attribution_overview","summary":"Which channels, sources and campaigns the attributed sessions came from, and what they were worth — the same cached rollup the Attribution page renders."},{"name":"audience_accepted","summary":"The people ACCEPTED into an audience, newest decision first — by the model from their professional profile (`automatic`, with its `reason`) or by the team: name, email, business email (their own address when it is at a work domain), title, employer, industry, last order."},{"name":"audience_account_watch","summary":"The audience's confirmed members (people the store's own role marks as members) worth a call: `late` — their reorder is overdue against their own ordering rhythm — and `browsing_without_buying` — identified on the site in the window with no order since."},{"name":"audience_digest","summary":"The audience's daily email — the people accepted since the last one, and those left to review: whether the person asking is subscribed, how many teammates are, and what was sent each of the last seven days — or why nothing was (not calibrated, the workspace's email is switched off, nobody new)."},{"name":"audience_overview","summary":"The whole pipeline for one audience, as the Audience Classification page shows it: identified (people who read its pages, 90 days), profiled (of those, with a professional profile / company record), classified (likely, how many of them read the pages, candidates, known accounts and how many became late), reviewed (accepted, rejected; `to_review` — likely the model's reading did not settle; `nothing_known` — readers nothing known about matches; and how the model's decisions stand: `automatic`, `overruled` by the team, `confirmed`), the latest calibration and whether the definition changed since, whether exports are on and why not, runs in progress, and this month's credits."},{"name":"audience_prospects","summary":"People browsing this site who look like a professional audience the store does not yet sell to — e.g."},{"name":"audience_rejected","summary":"The people REJECTED from an audience, newest decision first — by the model from their professional profile (`automatic`, with its `reason`) or by the team — with their title, employer and email."},{"name":"audience_run_estimate","summary":"Before starting an enrichment run: how many people it would look up and the most enrichment CREDITS (not money) it could spend."},{"name":"audience_runs","summary":"Recent enrichment runs for an audience: mode, status, credits spent, what was found, and — for calibration — how many known members and how many non-members the audience labels likely."},{"name":"causal_impact","summary":"Did a dated change actually CAUSE a metric to move? Fits a Bayesian interrupted time series: models the pre-change trend, predicts what would have happened without the change, and reports the difference with a credible interval."},{"name":"credit_usage","summary":"Credits used and left this month (UTC), every limit with when it resets and which runs out first, what started the lookups (audience runs, enrichment rules, a person, bulk lookups) with how many were found and how many were business contacts, and the latest lookups line by line."},{"name":"customer_accounts","summary":"The store's own customers by email, with the role the store gives them (provider, wholesale, …) and their LIFETIME order count, revenue and AOV."},{"name":"customer_counts","summary":"How many distinct people bought, how often they came back, and how many were new."},{"name":"data_coverage","summary":"What this workspace can and cannot answer for a window: order count, pixel volume, how many orders link to a pixel session, and which days the order sync missed."},{"name":"deletion_status","summary":"Check whether a deletion or opt-out request has been honoured for one email address, and when."},{"name":"discount_codes","summary":"Which discount codes were used, with orders, customers, revenue, discount given and AOV per code."},{"name":"dry_run_sync_rule","summary":"Show exactly which contacts a rule WOULD push, without pushing anything."},{"name":"enrichment_config","summary":"Who this workspace enriches and what it can still spend."},{"name":"esp_connection","summary":"Every app this workspace can connect — email platforms, the store, Slack, webhooks — with each one's status (connected, attention: set up but not working, not_connected, coming_soon)."},{"name":"esp_sync","summary":"Whether ESP sync is on (people are sent to the email platform only while it is), when it was turned on, who may be sent today counted by reason (subscribed / company email address / found work email) and how many wait for a known country, and where a workspace OWNER turns it on or off (fix_label)."},{"name":"get_account","summary":"Everything known about ONE account by domain: firmographics, its visitors, orders and ESP contacts."},{"name":"get_conversation","summary":"Read back one past conversation — its questions, answers and the caveats each answer carried."},{"name":"identification_status","summary":"Is identification actually running right now, and when did it last resolve anyone? Use when a workspace says visitors stopped appearing — a quiet pipeline and quiet traffic look identical on the accounts page, and this is what tells them apart."},{"name":"intent_pages","summary":"The page rules this workspace uses to score buying intent, and what share of its real traffic they match."},{"name":"journey_paths","summary":"How visitors move BETWEEN pages — the page-to-page graph behind the Journey view, reading the same nightly rollup the page reads."},{"name":"lapsing_accounts","summary":"Which of the store's OWN known buyers in a segment have stopped ordering."},{"name":"learned_plans","summary":"What this assistant has learned to do for recurring questions in this workspace — which tools it reuses, how many times each was confirmed, and which have been retired."},{"name":"list_alert_destinations","summary":"Slack destinations receiving this workspace's alerts, and whether each is active."},{"name":"list_api_keys","summary":"The workspace's API keys — id, name, prefix, last used."},{"name":"list_audiences","summary":"Every audience this workspace has defined — name, key, title and employer terms, pages, known-member segment — and `store_roles`: what set_audience's confirmed_roles may name (the store's roles, Geysera's, and each product sold, as 'product:<id>' labelled 'Bought …')."},{"name":"list_conversations","summary":"Your own past conversations in this workspace, most recently updated first, with each one's title, question count and when it was last touched."},{"name":"list_orders","summary":"The individual orders themselves, most recent first, with status, total, items, discount and the customer."},{"name":"list_team","summary":"Who has access to this workspace: members with their roles, invitations still pending, and how many seats are left."},{"name":"list_webhooks","summary":"Registered webhook endpoints and whether each is enabled."},{"name":"membership_conversion_paths","summary":"What is bought together."},{"name":"open_recommendations","summary":"The workspace's open recommended actions, from both the journey and attribution surfaces."},{"name":"order_sources","summary":"Where orders came from according to the STORE, and how they were checked out: the channel it attributed each order to (utm, organic, referral, typein), whether the order was a subscription renewal, the payment method and the shipping method — with orders and revenue for each, plus what shipping was charged and how much revenue shipped free."},{"name":"page_funnel","summary":"Per page: sessions that saw it, and how many added to cart, reached checkout and purchased in that same session."},{"name":"page_to_product_attribution","summary":"Which pages were seen in the same session as a purchase, optionally restricted to orders containing a given product."},{"name":"preview_enrichment_rule","summary":"Count an UNSAVED enrichment rule step by step — how many people each step leaves — before creating it."},{"name":"price_history","summary":"When a product's price actually changed."},{"name":"product_catalogue","summary":"The products the store SELLS — every item in the catalogue with its SKU, price, categories and stock status, whether or not it has ever sold."},{"name":"product_performance","summary":"Revenue, orders, units and quantity-weighted unit price per product."},{"name":"recall_facts","summary":"What this workspace has taught the assistant, and when."},{"name":"revenue_and_aov","summary":"Revenue, order count, AOV, discount and units per period (day/week/month)."},{"name":"search_accounts","summary":"The accounts board: search by name or domain, filter by classification and minimum intent, sort, paginate."},{"name":"search_visitors","summary":"Individual identified people, with the filters the Visitors page offers: text search, classification, minimum intent, whether they have ordered, whether they have been contacted, which intent page they hit, store segment and when they were last seen."},{"name":"subscription","summary":"This workspace's GEYSERA plan — what they pay us — its status, when it next renews, and whether it is set to cancel at the end of the period."},{"name":"sync_rule_targets","summary":"The lists an auto-sync rule can send to in a connected email platform, read live from it (marking the ones Geysera made), or — with weekly_csv — the owners and admins who may receive a weekly CSV."},{"name":"sync_rules","summary":"The auto-sync rules that push identified accounts to the connected ESP: their filters, status and last run."},{"name":"visiting_accounts","summary":"Which organisations visited the site, ranked by intent score, with their visits, visitors and any orders."},{"name":"visitor_enrichment","summary":"What enrichment found for ONE visitor, by their email address, and whether it has expired."},{"name":"visitor_journey","summary":"The pages ONE visitor viewed, in order, with time on each."},{"name":"workspace_setup","summary":"Is this workspace actually working? Store connection, onboarding progress, pixel status, identification health and the current plan and usage, in one call."}],"plans":[{"tier":"free","resolutions_per_month":150,"accounts":50,"enrichment_credits":0,"by_contract":false},{"tier":"starter","resolutions_per_month":300,"accounts":200,"enrichment_credits":150,"by_contract":false},{"tier":"pro","resolutions_per_month":1000,"accounts":750,"enrichment_credits":750,"by_contract":false},{"tier":"pro_plus","resolutions_per_month":2500,"accounts":2000,"enrichment_credits":2250,"by_contract":false},{"tier":"enterprise","resolutions_per_month":null,"accounts":null,"enrichment_credits":null,"by_contract":true}],"copilot_tool_reach":{"listed_here":55,"not_listed":55,"why_not_listed":"They change the workspace. An API key reaches read tools only, whatever it asks for — the planner's tool list is filtered by how you authenticated, so a key cannot name one. Listing them would advertise a capability that always refuses.","reachable_with":"A signed-in dashboard session. Changes are proposed first: the turn returns pending_confirmation describing the effect, and nothing runs until it is approved."},"actions":[{"method":"GET","path":"/","summary":"Root","tags":[],"required_scope":null},{"method":"GET","path":"/capabilities","summary":"Platform capability discovery","tags":["capabilities"],"required_scope":null},{"method":"GET","path":"/health","summary":"Health check","tags":[],"required_scope":null},{"method":"GET","path":"/health/liveness","summary":"Liveness check","tags":[],"required_scope":null},{"method":"GET","path":"/health/readiness","summary":"Readiness check","tags":[],"required_scope":null},{"method":"GET","path":"/signal-api/v1/accounts","summary":"List companies, one row per email domain. Consumer mailboxes share one \"Personal email\" row.","tags":["signal","public-api"],"required_scope":"read"},{"method":"GET","path":"/signal-api/v1/attribution","summary":"Visits, identifications and revenue by channel and campaign for a date range.","tags":["signal","public-api"],"required_scope":"read"},{"method":"GET","path":"/signal-api/v1/audiences","summary":"Audiences defined for this workspace","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"POST","path":"/signal-api/v1/audiences","summary":"Create an audience from its name","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"GET","path":"/signal-api/v1/audiences/summaries","summary":"One line per audience: the funnel counts a collapsed row shows","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"DELETE","path":"/signal-api/v1/audiences/{key}","summary":"Delete an audience","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"GET","path":"/signal-api/v1/audiences/{key}","summary":"One audience's definition","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"PUT","path":"/signal-api/v1/audiences/{key}","summary":"Create or replace an audience definition","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"GET","path":"/signal-api/v1/audiences/{key}/acceptance","summary":"The spec's acceptance tests, on this workspace's data","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"POST","path":"/signal-api/v1/audiences/{key}/acceptance/made-up-addresses","summary":"Look up 20 invented addresses; none should return a profile","tags":["signal","public-api","audiences"],"required_scope":"enrich"},{"method":"GET","path":"/signal-api/v1/audiences/{key}/account-watch","summary":"Confirmed members to call: late reorders, and back on the site without buying","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"PUT","path":"/signal-api/v1/audiences/{key}/automation","summary":"Turn daily lookups of an audience's new readers on or off","tags":["signal","public-api","audiences"],"required_scope":"enrich"},{"method":"GET","path":"/signal-api/v1/audiences/{key}/digest","summary":"The audience's daily email: who gets it and what was sent","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"PUT","path":"/signal-api/v1/audiences/{key}/digest","summary":"Subscribe yourself to the audience's daily email, or stop","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"GET","path":"/signal-api/v1/audiences/{key}/overview","summary":"The whole pipeline for one audience, stage by stage","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"GET","path":"/signal-api/v1/audiences/{key}/prospects","summary":"People who look like this audience, with the evidence","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"GET","path":"/signal-api/v1/audiences/{key}/reviews","summary":"People accepted or rejected, newest first","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"POST","path":"/signal-api/v1/audiences/{key}/reviews","summary":"Record the team's decision on one prospect","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"POST","path":"/signal-api/v1/audiences/{key}/reviews/clear","summary":"Withdraw the team's decision on one person","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"GET","path":"/signal-api/v1/audiences/{key}/runs","summary":"Recent runs for an audience, newest first","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"POST","path":"/signal-api/v1/audiences/{key}/runs","summary":"Look up the people in an audience, within a credit limit","tags":["signal","public-api","audiences"],"required_scope":"enrich"},{"method":"POST","path":"/signal-api/v1/audiences/{key}/runs/estimate","summary":"Who a run would look up, and the most it could spend","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"POST","path":"/signal-api/v1/audiences/{key}/runs/{run_id}/cancel","summary":"Stop a run; it makes no further paid lookups","tags":["signal","public-api","audiences"],"required_scope":"audiences"},{"method":"POST","path":"/signal-api/v1/copilot/ask","summary":"Ask a question about this workspace's commerce data","tags":["signal","copilot"],"required_scope":"copilot"},{"method":"POST","path":"/signal-api/v1/copilot/stream","summary":"Ask a question, streamed per analysis step","tags":["signal","copilot"],"required_scope":"copilot"},{"method":"GET","path":"/signal-api/v1/copilot/threads","summary":"List this workspace's conversations","tags":["signal","copilot"],"required_scope":"copilot"},{"method":"GET","path":"/signal-api/v1/copilot/threads/{thread_id}","summary":"A conversation and its messages","tags":["signal","copilot"],"required_scope":"copilot"},{"method":"GET","path":"/signal-api/v1/recommendations","summary":"Suggested next actions that have not been done yet.","tags":["signal","public-api"],"required_scope":"read"},{"method":"GET","path":"/signal-api/v1/visitors","summary":"List identified people with name, email, title and company. Contact fields are hidden on the free plan.","tags":["signal","public-api"],"required_scope":"read"},{"method":"GET","path":"/version","summary":"Version","tags":[],"required_scope":null}],"supported_flow_types":["cart_abandonment","checkout_abandonment","browse_abandonment","winback"],"inferred_flow_types":["browse_abandonment","cart_abandonment","checkout_abandonment","nurture","post_purchase","welcome_series","winback"],"integration_platforms":{"ecommerce":["shopify","woocommerce","bigcommerce"],"esp":["klaviyo","sendgrid","mailchimp","hubspot"]},"definitions":{"statuses":{"lead":{"label":"Lead","plural":"Leads","definition":"Not a customer yet. Everyone starts here."},"customer":{"label":"Customer","plural":"Customers","definition":"Has ordered from your store — set automatically from your orders — or marked a customer by your team."},"excluded":{"label":"Excluded","plural":"Excluded","definition":"Left out of everything: hidden from lists unless you choose to show them, and never sent to an email platform or a webhook, enriched, alerted on, or put in recaps, daily emails, plays or prospect lists. Use it for your own staff, vendors and test accounts."}},"status_order":["lead","customer","excluded"],"targetable_statuses":["lead","customer"],"default_status":"lead","status_scope":"Set a status on a company and everyone there has it; set one on a person and theirs wins. Someone on a personal address (gmail and the like) never takes a status from the mailbox provider.","audience":{"confirmed":{"label":"Confirmed","definition":"Your store's own role says they belong — for example an approved provider account."},"likely":{"label":"Likely","definition":"Their job title, employer or company record matches the audience. Worth reviewing; not confirmed."},"candidate":{"label":"Candidate","definition":"They read the audience's pages, but nothing known about them matches yet."},"accepted":{"label":"Accepted","definition":"Your team reviewed them and said yes. Audience sync rules send them."},"rejected":{"label":"Rejected","definition":"Your team reviewed them and said no."}},"intent_tags":["pricing","demo","product","cart","checkout","paid","blog","integrations","docs","other"],"default_min_intent_score":30,"cart_filter":{"setting":"Cart product","label":"Required product","short":"Required","definition":"Had a product named in a Cart product step of one of your enrichment rules in cart or checkout. Set them in Enrichment.","where_path":"/enrichment"},"domain_kinds":{"company":{"label":"Companies","basis":"Everything we could not place more precisely. This is the default, not a positive match."},"education":{"label":"Education","basis":"A .edu or .ac.uk address, or a US school-district name (kyschools.us, garlandisd.net)."},"government":{"label":"Government","basis":"A .gov or .mil address."},"healthcare":{"label":"Healthcare","basis":"The domain name contains a medical word (clinic, hospital, health, pediatric…). A guess from the name, so it holds health systems and suppliers as well as practices."},"isp":{"label":"Consumer ISP","basis":"A mailbox an internet provider hands out (comcast.net, rr.com). A person, not an organisation."},"personal":{"label":"Personal email","basis":"A consumer mailbox (gmail.com, yahoo.com). A person, not an organisation."},"disposable":{"label":"Disposable","basis":"A throwaway-address service."}},"channels":{"Paid Search":"Tagged as paid search, or arrived with a search ad's click id (gclid, msclkid).","Paid Social":"Tagged as paid social, or arrived with a social ad's click id (fbclid, ttclid).","Organic Search":"Came from a search engine's results, with no ad tagging.","Organic Social":"Came from a social network, with no ad tagging.","Email":"Tagged as email or newsletter.","Referral":"Came from another site that is not a search engine or a social network.","Affiliate":"Tagged as an affiliate or partner link.","Display":"Tagged as display or banner advertising.","Other Campaign":"Arrived with campaign tags we could not place. Somebody built the link on purpose, so it is never counted as Direct.","Direct":"We recorded their arrival and found nothing to name it: no campaign tags, no ad click id, no referrer — typed in, bookmarked, or from an app that strips the referrer. NARROWER THAN GA4's Direct, which also absorbs every arrival it cannot place. The visitors we could not place at all are counted above this table and left out of these shares rather than credited here."},"mailbox_kinds":["disposable","isp","personal"]}}