Skip to main content

Sandbox

The sandbox is a separate world, and it is meant to look like one. Each developer workspace, whether a team organization or your personal one, gets its own realm: a self-contained copy of Wefunder at an unguessable host under wefunder-sandbox.com, seeded with a company, an open round, and test users. Nothing in a realm touches live data, and no live Wefunder login works there.

A team organization gets its realm as soon as it is created. A personal workspace gets one when you create your first application, so its Sandbox tab is empty until then. Create an app and come back; your test client is registered in the realm as part of that step.

Test versus live​

Your application has two OAuth clients. The prefix on every identifier tells you which world it belongs to:

PrefixWorldWhere requests go
pk_test_, sk_test_, at_test_, rt_test_Sandbox realmapi.wefunder.com routes them to the sandbox cluster
pk_live_, sk_live_, at_live_, rt_live_Liveapi.wefunder.com routes them to production

The API host and the token endpoint are the same in both worlds. Only the authorize URL differs, because it is a browser page:

WorldAuthorize URL
Livehttps://wefunder.com/oauth/authorize
Sandboxhttps://oauth.wefunder-sandbox.com/oauth/authorize

A pk_test_ client sent to the live authorize URL gets an error page pointing at the sandbox one. There is no redirect between the two.

What is in your realm​

The Sandbox tab of the developer portal shows your realm's host and its starter data:

  • A seeded company with an open SAFE round, reachable at your realm host.
  • A syndicate, Sandbox Syndicate, that the founder manages, with one member (the second investor) and three deals: an open SPV deal you can send members to, a closed deal, and an acquired one. The open deal is a 506(b) round, so a test investor can attest accredited and finish checkout in the realm.
  • Test logins with their passwords: a founder who edits that company and manages the syndicate, and two investors with a seeded portfolio. Use them to complete the OAuth authorize flow in sandbox, to install your app on the company or the syndicate, and to browse the realm as those users.
  • Your pk_test_ client, already registered in the realm.

Because sandbox users are fixtures, the authorize screen in sandbox asks for the seeded login, never your real Wefunder password.

What works in sandbox​

The sandbox runs the same API with the same endpoints, scopes, error shapes, and access rules, on your realm's data. That includes installing your app on the realm's company or syndicate and minting the company- or syndicate-owned token the install stands for. The one thing that stays on live is the personal token from the portal's You row; use the sandbox OAuth flow for a test user token instead. The table is the exact line.

CapabilitySandbox (_test_)Live (_live_)
Client credentials with your pk_test_ / pk_live_ key: read:public, the realm's (or the live) public catalogYesYes
User OAuth (Authorization Code with PKCE) at the sandbox authorize URL, signing in as the seeded founder or investor. Needs an app with a redirect URIYesYes
User-token reads: GET /users/me, the seeded investor's investments, the seeded founder's company and its investmentsYesYes
The user install your OAuth consent records (shown to the user under Connected apps)Yes, in your realmYes
Installing on a company or syndicate: the install link, or POST /installations with a user token (which installs and mints in one call)Yes, on the realm's company or syndicate, signed in as (or with a token for) a seeded login who can edit it. Review is not needed in your own realmYes
Company- and syndicate-owned tokens minted from an install: POST /installations/{id}/tokensYes. They are at_test_ tokens and work only in sandboxYes
Revoking an install: DELETE /installations/{id}, or the company's or syndicate's Settings → Installed appsYes, in your realmYes
Personal tokens from the portal's You rowNo. Live only; use the sandbox OAuth flow for a test user tokenYes
Syndicate-owned OAuth token: the authorize request with grant_to=syndicate&syndicate_id=…, see Syndicate-owned tokensYes, for a syndicate in your realm that the signed-in test user can edit. As on live, it carries only read:syndicates / write:syndicates and is bound to that syndicate; the seeded founder manages the realm's syndicateYes
Webhook endpoints: list, fetch, the signed test send, re-enableYes, for your "mode": "test" endpointsYes
Webhook endpoints: create, update, delete, rotateNo. Through the live API, with "mode": "test" for sandbox. See Webhook setupYes
Member links: POST /offerings/{id}/member_links with a syndicate- or company-owned token, the deal page they open, and GET /member_links/{id}Yes, on the realm syndicate's open deal. There is no webhook for member links in either world: poll GET /member_links/{id}. See Member LinksYes
Join sessions: POST /syndicates/{id}/join_sessions and the hosted join pageYes. The hosted page is on your realm host. join_session.* and syndicate_member.* events go to the syndicate installYes (beta)
Investment sessions: POST /offerings/{id}/investment_sessions and the hosted landingYesYes (beta)
Checkout to a completed investmentCard works, with the Stripe test card 4242 4242 4242 4242. Bank account is not available in sandbox yet. Wire instructions are shownYes
Deliveries triggered by activity in your realmTo the installs in the realm, delivered to your app-wide "mode": "test" endpoints. investment.* and offering.* events go to installs on the offering's company. An app installed on a syndicate does not receive the investment events for the SPVs that syndicate leads unless it is also installed on the SPV's company. syndicate_member.* events go to installs on the syndicate, and join_session.* events go to the syndicate install that created the sessionYes, per install

Installing in your realm​

The portal's Installations page shows, under each install link, the same link on your realm host. Open it, sign in with the seeded founder from the Sandbox tab, and consent; or call POST /installations with a test user token from the sandbox OAuth flow. Either way the install lives in your realm: it never appears in the portal's live list, and you see and revoke it under the company's Settings → Installed apps in the realm. Tokens minted from it are at_test_ tokens, refused by live, and a live install's tokens are refused by the sandbox. Investor PII (read:investors:pii) follows the live rule: a sandbox install carries it only once Wefunder has approved your app for it.

What is different​

  • No money moves. Payment, identity, and signature gates that need a real person are driven by simulation tooling in the realm rather than by real providers.
  • Realm data is yours alone. Other developers cannot see it, and it is not a copy of production.

Walkthroughs​

Each walkthrough starts from a fresh realm and uses your pk_test_ client. Logins and passwords are on the portal's Sandbox tab. Ids below are placeholders: use the ones your realm returns.

Sign in a test user with OAuth​

  1. Send the browser to the sandbox authorize URL, https://oauth.wefunder-sandbox.com/oauth/authorize, with your pk_test_ client id, one of your redirect URIs, and a PKCE challenge. See Authorization Code with PKCE.
  2. Sign in as the seeded founder or an investor and consent.
  3. Exchange the code at the token endpoint. You get an at_test_ token.
  4. GET /users/me with that token returns the test user.

Install on the syndicate and send a member to its deal​

  1. Install your app on the realm syndicate as the seeded founder, and get the syndicate-owned at_test_ token. First tick the scopes these walkthroughs use on your app: read:installations, write:installations, read:syndicates, write:syndicates, read:invite_links, write:invite_links and write:investment_sessions. OAuth refuses a scope your app has not declared, and the install carries the scopes ticked when it is created. Then get a test user token for the founder from the OAuth walkthrough above, requesting read:installations write:installations, and either:

    • From the API. POST /installations with {"target_type": "syndicate", "target_id": "syn_…"}. Leaving out scopes asks for every scope your app declares. The response has the install under data and the syndicate-owned token under token, shown once. The credential is token.access_token.
    • From the install link. Open the install link on your realm host from the portal's Installations page, sign in as the founder and consent. Consent installs the app but hands you no token. Call GET /installations with the founder's token to find the install's inst_… id, then POST /installations/{id}/tokens to mint the syndicate-owned token (again token.access_token, shown once).

    The sandbox install is not listed in the live portal. You see and revoke it in the realm, under the syndicate's Settings → Installed apps.

  2. GET /syndicates/{syndicate_id}/deals with that token ({syndicate_id} is the syndicate's syn_… id) lists the open deal with status: "open". Its ofr_… id is the {offering_id} below.

  3. POST /offerings/{offering_id}/member_links with {"member_link": {"reference": "member_1"}}. The response has the link's url on your realm host and state: "created".

  4. Open the url in a signed-out browser. The deal page renders on your realm host and says you were invited to invest.

  5. Click Invest and sign up with a new email and password. The terms shown are the invited ones.

  6. Pay by card with 4242 4242 4242 4242 and finish checkout.

  7. GET /offerings/{offering_id}/member_links (or GET /member_links/{id}) shows the link with state: "invested", the new account's wefunder_user_id, and the investment_id.

No webhook tells you a link was bound or invested; poll the link. investment.* events go to installs on the offering's company, not to the syndicate install.

Bring a new member in with a join session​

  1. With the syndicate-owned token from the install above, call POST /syndicates/{syndicate_id}/join_sessions with {"join_session": {"email": "[email protected]", "success_url": "https://…"}}. The token needs write:syndicate_members, which write:syndicates includes.
  2. Open the session's url. The hosted join page is on your realm host.
  3. Sign up with the same email the session is bound to.
  4. Attest that you are accredited, then join.
  5. The browser returns to your success_url with status=joined.
  6. Your app-wide "mode": "test" endpoint receives syndicate_member.invited, syndicate_member.joined and join_session.completed. Create that endpoint first, through the live API with "mode": "test" (see Webhook setup).

Invest through an investment session​

  1. With the syndicate-owned token, call POST /offerings/{offering_id}/investment_sessions with {"investment_session": {"email": "[email protected]"}}. The install needs write:investment_sessions.
  2. Open the session's url to reach the hosted landing on your realm host, and create the account it asks for.
  3. Go through checkout and pay by card with 4242 4242 4242 4242.
  4. GET /investment_sessions/{id} shows status: "completed".

Resetting a realm​

Resetting a realm is not yet self-serve. If your seed data gets into a state you cannot recover from, write to support and we will re-seed it.

Promoting to live​

Nothing needs to be "switched" for the paths the sandbox covers. Your live client already exists and works on the companies and syndicates your organization manages. Swap _test_ credentials for _live_ ones, point users at the live authorize URL, and install again on live: sandbox installs and their tokens stay in the realm. Installing your app on someone else's company or syndicate requires app review from the portal.

note

Sandbox realms exist for the public API described on this site. The Partner SPV simulation endpoints under /partner/sandbox/… belong to the invite-only Partner preview and are not part of the public surface.

Was this page helpful?