Syndicate API
Manage a syndicate from your own tools: the member directory, live deals, aggregate statistics, the portfolio, and the syndicate's settings. Built for syndicate leads and the tools and agents they connect.
The Syndicate API is in beta. Fields may be added; breaking changes follow the deprecation policy.
What you can do
| Area | Capabilities |
|---|---|
| Syndicate | List the syndicates you belong to, read one, update its name, description, and tagline |
| Members | List and search the directory, invite, approve applicants, promote, demote, exile, restore, reorder, export CSV, read a member's investments |
| Deals | List live deals, read one, list the members who invested in it, propose close or finalize (human-approved) |
| Statistics | Aggregate metrics: members by role, deals, total raised, distinct investors, recent activity |
| Portfolio | Point-in-time totals and one aggregated position per deal |
Removing a member, closing a deal, and finalizing a deal are irreversible, so they run through intents: the API records a proposal and a permitted human approves it on Wefunder.
Authentication
Syndicate endpoints accept two kinds of principal. A client_credentials key is neither: it has no user, holds only read:public, and cannot call these endpoints.
A user token (Authorization Code + PKCE) acts as the user who authorized it, with exactly that user's standing in the syndicate, re-read on every request. It sees every syndicate the user belongs to unless it is bound to one syndicate, in which case it reaches only that one.
A syndicate-owned token acts as the syndicate itself, not as a person, so it keeps working when the person who set it up leaves the team. There are three ways to get one, and all three are bound to the syndicate, hold at most read:syndicates and write:syndicates, and work only on the /syndicates endpoints (any other path answers 403 with Organization tokens can only access syndicate endpoints):
| How | Where its tier and scope ceiling come from |
|---|---|
| Generate API key in the syndicate's Apps & API settings | The install the key creates for your account on the syndicate, capped at your own tier (an operator's key is an operator key, whatever the rollout state). The key's scopes are cut to read:syndicates (plus write:syndicates if requested) and to the install's scopes |
| Installing your app on the syndicate and minting an installation token | The install's granted tier and scopes, frozen when the install was made |
The OAuth flow with grant_to=syndicate&syndicate_id=syn_… (details) | Your app's active install on that syndicate, if it has one. Without an install, what the token may do depends on a rollout Wefunder controls; see whose authority a token carries. Install your app rather than relying on that fallback |
For a syndicate-owned token the install is the authorization. Its tier (full access or operator; see Permission model) decides which operations the token may perform, and its scopes are a ceiling on the token's own: an install granted only read:syndicates cannot write even if the token string also carries write:syndicates (the request fails as insufficient_scope). Revoking the install invalidates its tokens: every token minted from the install, and every token of the installing app bound to that syndicate (a settings API key or a grant_to=syndicate OAuth token), is revoked with it, and the next request answers 401. A token minted from the install is also refused with 401 The installation this token was issued for has been revoked the moment the install is inactive, even if the sweep has not caught up. Treat 401 as the revocation signal and stop retrying. The capability check only applies to a syndicate-owned token that still authenticates but has no active install behind it: a grant_to=syndicate token for an app that was never installed, or one the sweep has not reached yet. For such a token, while Wefunder enforces installations for syndicate-owned tokens, GET /syndicates returns an empty list and every other syndicate endpoint answers 403 This application is not installed on this syndicate; before that enforcement, it acts with full access within its scopes (see whose authority a token carries). A syndicate-owned token used on a different syndicate answers 403 Token is not owned by this syndicate. The person who authorized the token confers nothing on it, not even when they are a Wefunder admin; the API does refuse the token if that person's Wefunder account is deleted or locked.
Revocation differs by kind: a user revokes a user token from Settings → Connected apps; a current manager revokes a syndicate-owned token from the syndicate's Apps & API settings or by revoking the install under Installed apps, which sweeps every token minted from it. Lifetimes and the full table of credential kinds are in Security model.
All requests go to https://api.wefunder.com with no version in the path. Pin the API version with the Wefunder-Version header; see Versioning.
curl "https://api.wefunder.com/syndicates/syn_aB3xQ9k2vF8mNp1zT5wY7Qc4/members" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Wefunder-Version: 2026-10-01"
Scopes
| Scope | Grants |
|---|---|
read:syndicates | Syndicates, members, deals, statistics, portfolio, CSV export |
write:syndicates | Settings, member management, and proposing close and finalize intents |
There is no separate "dangerous" scope. Irreversible actions are gated by intents, not by a scope.
Permission model
Every principal resolves to one of three tiers, and the tier decides what the token can do. A user token takes its tier from the user's role in the syndicate, exactly what that user could do on the web; a syndicate-owned token takes its tier from the install's grant (see Authentication):
| Standing in the syndicate (user token) | Install grant (syndicate-owned token) | Tier | Can |
|---|---|---|---|
Creator, or a manager with full_access | full_access | full access | Everything below, plus promote or demote managers, propose close and finalize, and approve intents |
Manager with operator permission | operator | operator | Read everything including moderator-only fields, invite, approve applicants, exile, restore, reorder, update titles and carry, export CSV |
| Any other membership row (member, backer, applicant, invitee) | — (a syndicate install is always full_access or operator) | view only | Read the syndicate, the directory without moderator-only fields, deals, and statistics |
| No membership | No active install | none | 403 on every endpoint |
Wefunder site admins hold full access on every syndicate only through Wefunder's internal tools, whose user tokens carry admin:write (view only with admin:read). Through any other application a site admin has their own role, like anyone else. Two extra rules apply to writes on individual members: nobody can act on the creator's own membership, and only the creator can grant full_access. Approving an intent is a human action on Wefunder, so a syndicate-owned token can propose a close or finalize at full access but the approval still comes from a person with full_access.
A 403 names the reason: You do not have access to this syndicate (user token with no membership), This application is not installed on this syndicate (a syndicate-owned token that still authenticates but has no active install; a revoked install's own tokens get 401 instead) or Token is not owned by this syndicate (syndicate-owned token on another syndicate), Token not bound to this syndicate (a user token bound elsewhere), or You do not have permission for this operation (tier too low).
Identifiers
Every resource is addressed by its prefixed id, never an integer:
| Resource | Prefix | Example |
|---|---|---|
| Syndicate | syn_ | syn_aB3xQ9k2vF8mNp1zT5wY7Qc4 |
| Member | mem_ | mem_7Hq2kLm9Pw3xZr5tYv8bNc4d |
| Deal (offering) | ofr_ | ofr_kUlkWcWVugo7cSLD4RdD6gdK |
| User | usr_ | usr_8Kd0aB3xQ9k2vF8mNp1zT5wY |
Deal ids are the same ofr_ ids the Explore API returns for an offering. Attributes that end in _id and hold an integer (user_id, company_id, fundraise_id, created_by_user_id) are deprecated; use the string reference beside them (user, company, offering, created_by).
Monetary values
Investment amounts (amount, amount_raised, investment_total, total_amount, total_raised, min_investment, max_investment) are whole US dollars as strings: "15000" is $15,000. A deal's funding_target is cents as a string: "50000000" is $500,000.00. The portfolio endpoints use integer cents throughout and say so on every field name (*_cents).
Response format
Every endpoint returns a data envelope. Lists carry pagination in meta.
{
"data": {
"id": "syn_aB3xQ9k2vF8mNp1zT5wY7Qc4",
"type": "syndicate",
"attributes": {
"name": "Acme Syndicate",
"slug": "acme-syndicate",
"tagline": "Backing the best in deep tech",
"description": "A syndicate for Acme investments",
"avatar_url": "https://uploads.wefunder.com/uploads/club/icon/42/large_avatar.png",
"syndicate_type": "opt_in_deal_by_deal",
"published": true,
"launched": true,
"membership_open": true,
"auto_accept_accredited_members": false,
"auto_approve_join_sessions": true,
"fund_required": false,
"primary_fund_company_id": null,
"primary_fund_company": null,
"member_count": 47,
"deal_count": 2,
"created_by_user_id": 123,
"created_by": "usr_8Kd0aB3xQ9k2vF8mNp1zT5wY",
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2026-03-01T14:00:00Z"
}
}
}
deal_count counts live deals (open, oversubscribed, or closing), the same set GET /syndicates/:id/deals returns and the same number as live_deals in statistics; see which rounds are deals for what that set leaves out. syndicate_type is opt_in_deal_by_deal, annual_subscription, traditional_fund, or null. The two fund types carry an extra object of the same name with their configuration.
Endpoints
Syndicates
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /syndicates | read:syndicates | Syndicates the user belongs to. For a syndicate-owned token: the owning syndicate (an empty list only for a still-valid token with no active install behind it; a revoked install answers 401) |
GET | /syndicates/:id | read:syndicates | One syndicate |
PATCH | /syndicates/:id | write:syndicates | Update name, description, tagline (operator or above) |
PATCH takes {"syndicate": {"name": "...", "description": "...", "tagline": "..."}} and returns the updated syndicate.
Members
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /syndicates/:id/members | read:syndicates | List and search the directory |
POST | /syndicates/:id/members/invite | write:syndicates | Invite by email |
PATCH | /syndicates/:id/members/:member_id | write:syndicates | Update title and carry override |
POST | /syndicates/:id/members/:member_id/approve | write:syndicates | Approve an applicant |
POST | /syndicates/:id/members/:member_id/hide | write:syndicates | Exile a member |
POST | /syndicates/:id/members/:member_id/promote | write:syndicates | Promote to manager with full_access (creator only) |
POST | /syndicates/:id/members/:member_id/demote | write:syndicates | Reduce a manager to operator |
POST | /syndicates/:id/members/:member_id/restore | write:syndicates | Restore an exiled or resigned member |
POST | /syndicates/:id/members/:member_id/resend_invite | write:syndicates | Resend the invite email |
POST | /syndicates/:id/members/reorder | write:syndicates | Set directory order |
GET | /syndicates/:id/members/:member_id/investments | read:syndicates | A member's investments in syndicate deals (moderators only) |
POST | /syndicates/:id/members/export_csv | read:syndicates | Download the directory as CSV (moderators only) |
DELETE | /syndicates/:id/members/:member_id | write:syndicates | Always 422 use_intents; propose syndicates.remove_member instead |
Deals
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /syndicates/:id/deals | read:syndicates | Live deals |
GET | /syndicates/:id/deals/:fundraise_id | read:syndicates | One deal |
GET | /syndicates/:id/deals/:fundraise_id/investors | read:syndicates | Members who invested in the deal (moderators only) |
POST | /syndicates/:id/deals/:fundraise_id/close | write:syndicates | 2026-10-01 only (removed in 2026-10-15): propose a syndicates.close_deal intent (full access) |
POST | /syndicates/:id/deals/:fundraise_id/finalize | write:syndicates | 2026-10-01 only (removed in 2026-10-15): propose a syndicates.finalize_deal intent (full access) |
Statistics and portfolio
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /syndicates/:id/statistics | read:syndicates | Aggregate metrics |
GET | /syndicates/:id/portfolio | read:syndicates | Portfolio totals |
GET | /syndicates/:id/portfolio/positions | read:syndicates | One position per deal, paginated |
Related pages
- Members
- Deals
- Statistics
- Portfolio
- Intents, how irreversible actions are approved
- Activity, the audit trail of what your tools did
- Quickstart: Connect an AI Agent to Your Syndicate