references/api-cheatsheet.md
# GitBook API cheatsheet
All endpoints are at `https://api.gitbook.com/v1`. Authentication is `Authorization: Bearer $GITBOOK_TOKEN` on every request. JSON throughout.
This cheatsheet is scoped to what the `configure-site` skill actually needs. The full API surface is much larger — for less common endpoints, search the GitBook docs directly.
## Auth and discovery
`GITBOOK_TOKEN` should already be set in the environment by the time you reach this cheatsheet — see the token acquisition flow in `SKILL.md` (prompt the user to paste a personal access token created at https://app.gitbook.com/account/developer). Never store it on disk, never echo it back, never commit it.
```bash
# Verify token and get the authenticated user
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/user
# List the user's organizations
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs
```
When the user has multiple orgs, **show the list with org titles and ask them to confirm by name**, even if there's only one — site creation in the wrong org is a real and visible mistake. Save the chosen `organizationId` for the rest of the session.
## Sites
### Create a site
```bash
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "My Product Docs",
"type": "basic",
"visibility": "public"
}' \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites
```
Request body fields:
- `title` (required, 2–128 chars)
- `type` — `basic | premium | ultimate | sponsored`. Defaults to `basic`. Premium/ultimate features (custom logos, custom fonts, etc.) require a paid plan.
- `visibility` — `public | unlisted | share-link | visitor-auth`. Defaults to `public`.
- `spaces` — optional array of existing space IDs to link immediately. Omit when creating a fresh site without pre-existing spaces.
Response is the full `Site` object. Save `id` for subsequent calls; the `urls.app` is the dashboard URL the user can open. The response also carries `urls.preview` (a rendered preview of the site's draft/in-progress content) and, once published, `urls.published` — worth surfacing alongside `urls.app` when the user asks "what will this look like." This is also the field that makes change-request preview links possible; see the `cr-create` skill's "Surfacing the preview link" for the full space→site resolution when you only have a space ID.
### List sites in an org
```bash
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites
```
### Get / update a site
```bash
# Get
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID
# Update (PATCH)
curl -s -X PATCH -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "New Title"}' \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID
```
## Spaces
### Create a space (in an org)
```bash
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Guides",
"emoji": "📚",
"visibility": "in-collection"
}' \
https://api.gitbook.com/v1/orgs/$ORG_ID/spaces
```
Notes:
- `title` is required (max 50 chars)
- `emoji` is optional but useful for nav
- `visibility` for site spaces is typically `in-collection` (visibility is then controlled by the site)
- The created space starts empty; you'll connect it to git or import content separately
### Get a space / list a space's tree
```bash
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/spaces/$SPACE_ID
# Get the content tree (pages and groups)
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/spaces/$SPACE_ID/content
```
### Get the Git Sync state of a space
```bash
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/spaces/$SPACE_ID/git/info
```
Returns `{repoName, installationProvider: "github" | "gitlab", integration, url, updatedAt}` when sync is set up; 404 when not. Use this to verify the user finished the UI handoff. There's no equivalent site-level status endpoint yet — verify a site-wide setup by checking each of its spaces this way.
**There is effectively no API to *set up* Git Sync yet.** This is the load-bearing constraint of this whole workflow. There is an operation, `installGitSyncProviderOnTarget`, that accepts either a site or a space as its target — but connecting the GitHub/GitLab account still requires OAuth in the GitBook app, and as of this writing the operation isn't exposed through the GitBook MCP server (`search` for it returns nothing). GitBook is exploring a reusable "connection" that could be set up once and driven by API afterward, but that isn't available yet. Don't design a flow around it — always route Git Sync setup through the UI handoff (`references/git-sync-handoff.md`), and re-check `search`/`describe_operation` occasionally if you want to confirm whether this has changed.
## Site sections, section groups, and site-spaces
A site's navigation is structured as one of three shapes:
1. A flat list of site-spaces (no sections), or
2. A list of sections, each containing one or more site-spaces, or
3. A list of section groups and/or sections, with section groups containing nested sections (used to bucket related sections in the top nav)
A **section** is a unit of top-nav navigation; a **section group** wraps related sections; a **site-space** is the binding between a section and a space.
### Sections vs. site-spaces — pick the right one
This is the most common API confusion. The TL;DR:
- If the spaces are **semantically distinct** (the common case — "Guides", "API Reference", "Changelog" are different bodies of content) → **use sections**. Each section gets its own top-nav tab and URL slug.
- If the spaces are **variants of the same content** → use site-spaces directly. The canonical use case is auto-translation (one English space, plus auto-translated French/German/Japanese variants — all at the same URL with a language switcher). It's a niche feature.
Defaulting to sections is almost always correct. If you find yourself reaching for `POST /site-spaces` for unrelated content, you probably want `POST /sections` instead.
### Add a section to a site
The pattern is **POST + PATCH** because the POST silently drops some fields:
```bash
# Step 1: POST creates the section with title, icon, and the linked space
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"spaceId": "<existing-space-id>",
"title": "Developers",
"icon": "code"
}' \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/sections
# → returns the created section with id="<section-id>"
# Step 2: PATCH sets the description (POST silently drops it)
curl -s -X PATCH -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "API reference, SDKs, and webhooks."
}' \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/sections/<section-id>
```
Required on POST: `spaceId` (existing space). Strongly recommended: `title` (2–128 chars), `icon` (Font Awesome name like `code`, `house`, `id-card`, `clock-rotate-left`).
**`description` must be set via PATCH after creation** — POSTing it inline is accepted by the API but the value is dropped from the created section. Same applies to `localizedTitle` and `localizedDescription` (language→string maps).
The created section also gets `path` (URL slug derived from title), `default`, `draft`, and a `siteSpaces` array.
### The auto-created wrapper section gotcha
When you create a site with `type: site` and then add spaces via `POST /site-spaces` (instead of `POST /sections`), GitBook auto-creates a wrapper section named after the site itself to contain them. If you later want to convert to a proper sections-based layout, you have to:
1. Create the new sections you actually want.
2. `DELETE` the auto-created wrapper section.
Conversely, if you start with sections from the beginning, this never appears. **Default to creating sections explicitly from the start** to avoid the cleanup later.
### Add an existing space to a site as a site-space (translation variants)
For the auto-translation variant case only:
```bash
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"spaceId": "<existing-space-id>",
"sectionId": "<optional-section-id>"
}' \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/site-spaces
```
If `sectionId` is omitted, the space is added at the root level (or in the default section). For unrelated content, **use sections instead** — see above.
### Section groups
When a site has 3+ closely related sections, they can be wrapped in a **section group** (e.g. "Products" wrapping Payments / Identity / Connect). The structure response shows these as objects with `"object": "site-section-group"` containing a `sections` array. Section groups are typically created and reordered through the GitBook UI today; the API documentation should be checked for current support before assuming a specific endpoint shape.
### Get the full site structure
```bash
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/structure
```
Returns `{type: "sections" | "siteSpaces", structure: [...]}`. Each entry in `structure` has an `object` field — `site-space`, `site-section`, or `site-section-group` — and is recursive (groups can contain more groups).
For each `site-section`, the `siteSpaces` array contains one entry per language. Only the language that originated from Git has a populated `gitSync` field on its space; the rest are auto-translated and live entirely in GitBook. See `references/example-site/structure.json` for a complete real-world example.
## Customization (branding)
The customization endpoints are the meat of the branding workflow. The schema is large — there are recipe examples in `customization-recipes.md`; this section just shows the mechanics.
### Get current customization
```bash
# Site-wide (the default that applies to all spaces)
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/customization
# Per-site-space override
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/site-spaces/$SITE_SPACE_ID/customization
```
### Update customization
The customization endpoint expects a *full* `SiteCustomizationSettings` payload. The safe pattern is read-modify-write:
```bash
# 1. Get current
CURRENT=$(curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/customization)
# 2. Modify in jq (or in a small script)
NEW=$(echo "$CURRENT" | jq '.styling.primaryColor = {"light": "#0E5BFF", "dark": "#5B8CFF"} | .styling.theme = "clean"')
# 3. PUT it back
curl -s -X PUT -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d "$NEW" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/customization
```
### Reset a per-site-space override
```bash
curl -s -X DELETE -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/site-spaces/$SITE_SPACE_ID/customization
```
This drops the override; the site-space falls back to the site-wide settings.
## Imports (alternative to Git Sync)
When the user wants to ingest existing external content quickly:
```bash
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "website",
"url": "https://docs.example.com"
},
"target": {
"space": "<spaceId>"
},
"enhance": true
}' \
https://api.gitbook.com/v1/org/$ORG_ID/imports
```
Source can also be `{"type": "file", "files": [...]}` for a file-based import. The `enhance: true` flag has GitBook's AI clean up the imported content. Returns a `ContentImportRun` with an `id` you can poll.
## Apply a space template
```bash
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "<template-id>"}' \
https://api.gitbook.com/v1/spaces/$SPACE_ID/content/template
```
Templates are a quick way to seed an empty space with reasonable starter content. The template ID must be one the org has access to.
## OpenAPI specs
API reference spaces should auto-generate their endpoint pages from an OpenAPI spec rather than have them hand-authored. The spec is registered at the org level and referenced from a space's `SUMMARY.md` via `type: builtin:openapi`.
### Register a spec from a URL
The simplest path — the spec lives somewhere reachable (GitHub Pages, raw file in a repo, your own CDN), and GitBook fetches it on a schedule.
```bash
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"slug": "lyra-feedback-v1",
"source": {
"url": "https://lyra-app.github.io/api/v1/openapi.yaml"
}
}' \
https://api.gitbook.com/v1/orgs/$ORG_ID/openapi
```
Response includes the spec object with `id`, `slug`, `processingState`. The slug is what goes into the space's SUMMARY.md.
### Register a spec by uploading the file directly
Use this when the spec isn't hosted publicly, or when the source-of-truth should live in GitBook rather than a separate repo.
```bash
curl -s -X POST -H "Authorization: Bearer $GITBOOK_TOKEN" \
-F "slug=lyra-feedback-v1" \
-F "file=@./openapi.yaml" \
https://api.gitbook.com/v1/orgs/$ORG_ID/openapi
```
### List, get, update, delete
```bash
# List specs in an org
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/openapi
# Get one spec
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/openapi/$SPEC_ID
# Update (replace) a spec — same body shape as create
curl -s -X PATCH -H "Authorization: Bearer $GITBOOK_TOKEN" ...
# Delete
curl -s -X DELETE -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/orgs/$ORG_ID/openapi/$SPEC_ID
```
### Two upload strategies — when to pick which
| Strategy | Best when | Cons |
|---|---|---|
| URL-based (GitBook fetches from your URL) | Spec is auto-generated by CI in your repo, has its own review process, or needs to live alongside other developer assets | GitBook only refreshes on a schedule or manual trigger; broken/unreachable URL means no docs |
| Direct upload (file goes into GitBook) | Spec is hand-authored, doesn't change often, or you want GitBook to own the canonical copy | No automatic refresh from upstream; updates require a re-upload |
**Ask the user which they prefer.** Both are valid; assuming one is wrong roughly half the time.
### Reference the spec from SUMMARY.md
Once the spec is registered, link to it from the space's `SUMMARY.md`. The pattern uses a fenced YAML block as the bullet content rather than a regular link:
````markdown
## Feedback API
* [Overview](feedback-api/README.md)
* ```yaml
type: builtin:openapi
props:
models: false
downloadLink: true
dependencies:
spec:
ref:
kind: openapi
spec: lyra-feedback-v1
```
````
`spec: lyra-feedback-v1` matches the slug from the registration step. `models: true` includes a separate "Models" page; `downloadLink: true` adds a "Download spec" button to the rendered pages.
GitBook expands this entry at render time into one nav item per operation in the spec, grouped by tag. The hand-authored `Overview` page above stays as prose context (base URL, version policy, what this resource is for), with the auto-generated operation pages alongside it.
## Error handling notes
- **401 Unauthorized** — bad PAT. Surface this clearly; don't retry silently.
- **403 Forbidden** — usually a permission issue (PAT belongs to a user without access to the org/site, or the feature requires a higher site plan).
- **404 Not Found** — wrong ID, or the resource was deleted. For sites/spaces this is permanent after 7 days.
- **409 Conflict** — usually means the requested operation would not change state (e.g. transferring a space to its current org).
- **412 Precondition Failed** — operation can't be done in the current state.
- **400 Bad Request** — schema problem; the response body usually says which field.
When a customization update fails with 400, log the offending field and check `customization-recipes.md` for the right shape — it's almost always a missing required nested field or a wrong color format.references/block-ecosystem.md
# Block ecosystem — choosing the right one
GitBook has a rich block ecosystem. A docs site that uses it well feels like a real product; one that doesn't feels like a `man` page. The skill's instinct when generating content should be to **reach for the specialized block**, not the markdown-y plain-text fallback. This reference catalogues the high-leverage blocks and the patterns they belong to.
`write-docs` is the authority on the syntax of each block. This file is about *which block to use when*, with example invocations to anchor each pattern.
## Mirror the source before inventing
When you're rebuilding from existing docs (a competitor's site, the user's previous platform, an internal wiki), **fetch the source and mirror its landing pages and information architecture before reaching for novel templates.** The user's existing IA is the spec — they almost always have reasons for it that aren't visible in a pile of markdown files. Inventing new card grids, hero blocks, and layouts when the source already had a working answer is how you produce something that *looks* GitBook-y but doesn't actually serve the readers.
The right default order:
1. **Look at the rendered source** — fetch the live site, screenshot the landing page, count the cards, note what's at the top of each space.
2. **Decide what carries over verbatim** vs. what genuinely needs updating. A "What's new" section that listed three product launches still wants to list those three launches.
3. **Then apply GitBook idioms** — convert the source's hand-rolled HTML cards to a `<table data-view="cards">`, swap inline icons for Font Awesome equivalents, replace ASCII diagrams with Mermaid. The block ecosystem upgrade should be additive to the IA, not a replacement for it.
Reaching for templates from this file's decision table without first mirroring the source produces pages that show off GitBook but don't serve the original's readers. Mirror first, upgrade second.
## The decision table
| Content shape | Right block | Wrong-default to avoid |
|---|---|---|
| Release notes / changelog / "what's new" | `{% updates %}` with `{% update %}` entries (RSS auto-generated, supports tags) | Bare `## YYYY-MM-DD` headings followed by bullet lists |
| API endpoint reference | OpenAPI auto-generation via `type: builtin:openapi` in SUMMARY.md | Hand-authored endpoint pages duplicating the spec |
| State machines, flows, sequences, simple architecture | ` ```mermaid ` fenced block | ASCII art with `-->` and box-drawing characters |
| Tabular comparison or reference | Markdown table | Repetitive prose with parallel structure |
| Sequential walkthrough (3+ ordered steps with substantial content per step) | `{% stepper %}` with `{% step %}` entries | Numbered list with all steps in one paragraph each |
| Equivalent code in different languages or environments | `{% tabs %}` with `{% tab %}` per language | Stacked fenced code blocks with `### Node`/`### Python` headings |
| "Heads up" / "danger" / "tip" callouts | `{% hint style="info\|warning\|danger\|success" %}` | Bold-italic prose like "**Note:** ..." |
| Side-by-side content layout | `{% columns %}` with `{% column %}` entries | Two paragraphs and hoping for the best |
| Pickable card grid (e.g. "choose your path") | Card-table — `<table data-view="cards">` | Plain bullet list with sub-bullets describing each option |
| Cross-cutting boilerplate that shouldn't be copy-pasted | `{% include "../.gitbook/includes/<name>.md" %}` | Duplicated paragraphs across pages |
| Values that appear on many pages (env URLs, support emails, version pins) | Variables in `.gitbook/vars.yaml` + `<code class="expression">space.vars.<name></code>` | Hard-coded literals in every page |
| Long detail that's optional reading | `{% expandable %}` (collapsible details) | A whole separate page just for the digression |
| Video / external preview embeds | `{% embed url="..." %}` | Plain link to YouTube |
| Conditional content for different visitor personas | `{% if visitor.claims... %}` ... `{% endif %}` | Static content that ignores audience |
| Marketing-style landing page or hero | `layout: width: wide` + `cover:` + `coverY:` in frontmatter | Always-wide on every homepage — the default width is right for normal docs landings |
The skill should treat the right column as a smell — when generating content, if it falls into one of the wrong-default patterns, that's a signal to switch to the specialized block on the left.
## Block-by-block guidance
### Updates block — for any changelog or release-notes content
The Updates block produces a timeline view with auto-generated RSS, optional tags for filtering, and a richer card-style layout than plain headings:
```markdown
{% updates format="full" %}
{% update date="2026-04-22" tags="api,beta" %}
## AI topic auto-classification (beta)
Lyra now assigns incoming Feedback to existing Topics based on content. Enable
per-Topic from **Settings → AI** in the dashboard. The classifier runs on every
new Feedback within a few seconds of arrival.
{% endupdate %}
{% update date="2026-03-30" tags="auth,sso" %}
## SAML SSO for Pro and Enterprise
Configure under **Settings → Authentication**. Supports Okta, Azure AD, and any
SAML 2.0 IdP. SCIM provisioning is available on Enterprise.
{% endupdate %}
{% endupdates %}
```
The `format` can be `full` (cards), `short` (compact), or `numeric` (numbered). Tags are defined in `.gitbook/tags.yaml`:
```yaml
- tag: api
label: API
icon: code
- tag: auth
label: Authentication
icon: lock
- tag: beta
label: Beta
icon: flask
```
**Always use Updates for changelog spaces.** The auto-generated RSS feed alone is worth the small extra syntax cost.
### Mermaid — for any flow, sequence, state machine, or simple architecture
Standard markdown fenced block with `mermaid` as the language:
````markdown
```mermaid
flowchart LR
Pending --> Authorized --> Captured
Pending -.->|declined| Failed
Authorized -.->|voided| Voided
Captured -.->|refund| Refunded
Captured -.->|disputed| Disputed
```
````
GitBook renders it inline. Mermaid supports `flowchart`, `sequenceDiagram`, `stateDiagram-v2`, `erDiagram`, `gantt`, `classDiagram`, and `pie`. For docs sites, `flowchart` and `sequenceDiagram` cover ~90% of cases.
**Reach for Mermaid whenever you'd otherwise draw boxes-and-arrows in ASCII.** The ASCII version is uglier, harder to maintain, and not screen-reader friendly.
### OpenAPI — for API reference spaces
When building an API reference space, **don't hand-author endpoint pages**. Generate or upload an OpenAPI spec and let GitBook auto-generate the operation pages. Hand-authored endpoint pages drift from the actual API and triple your maintenance burden.
The flow has three pieces:
**1. Get an OpenAPI spec into GitBook.** Two options:
- **Upload via the GitBook API**: `POST /v1/orgs/{orgId}/openapi` with the spec. The skill can do this — see `api-cheatsheet.md`.
- **Host on GitHub Pages or any URL**: GitBook fetches it on a schedule. Lower-friction for teams who already host their spec publicly.
Either way the spec gets a slug (e.g. `lyra-v1`) that you reference from the SUMMARY.md.
**2. Reference the spec from SUMMARY.md** to auto-generate the operation pages. The pattern from the example site:
````markdown
## Feedback API
* [Overview](feedback-api/README.md)
* ```yaml
type: builtin:openapi
props:
models: false
downloadLink: true
dependencies:
spec:
ref:
kind: openapi
spec: lyra-v1
```
````
The fenced YAML inside the SUMMARY bullet expands at render time into one nav entry per operation in the spec, grouped by tag. Set `models: true` to include schemas as their own pages; `downloadLink: true` adds a "Download spec" button.
**3. Write a brief overview README.md** for each API resource — prose context that doesn't belong in the spec: base URL, version policy, what the resource is for, migration notes from earlier versions. The auto-generated operation pages live alongside this README in the nav.
When the user is creating an API reference space, **the skill should ask whether they have an OpenAPI spec already**. If yes, route through the upload flow. If no, offer to generate a starter spec from whatever endpoint information the user has — even a minimal spec is much better than hand-authored pages that immediately drift.
### Layout in frontmatter — especially for homepages
Every space's homepage `README.md` should set layout flags. The default page width is narrow (optimised for prose); landing pages and overviews want wider layout, fewer chrome elements, often a cover image:
```yaml
---
description: Build with Lyra. Customer feedback, structured.
icon: house
cover: .gitbook/assets/home-cover.png
coverY: 0
layout:
width: wide
cover:
visible: true
size: hero
title:
visible: true
description:
visible: true
tableOfContents:
visible: false
outline:
visible: false
pagination:
visible: false
---
```
**`width: wide` is for marketing-style or hero pages, not the default for documentation landings.** Use it when the page has a hero image, a large card grid that needs the room, or a multi-column dashboard layout. For a normal docs landing — even one with card-tables for navigation — the GitBook default width with TOC visible reads better and matches user expectations for a docs site. When in doubt, **mirror the source.** If you're rebuilding from another platform's docs site, look at how the original landing renders before deciding to widen it.
Use `width: wide` deliberately on:
- Marketing-style landing pages with hero imagery
- Changelog pages where the Updates timeline benefits from horizontal space
- Dashboard-style pages with side-by-side panels
- Pages with genuinely wide tables that don't fit at default width
For interior content pages, `width: default` (or omitting the layout block entirely) is the right call — long-form prose reads better at narrower widths.
### Reusable content (`.gitbook/includes/`)
For boilerplate that appears in 3+ places (test/live mode warnings, persona switchers, footer disclaimers, version banners), extract it into `.gitbook/includes/<name>.md` and pull it in:
```markdown
{% include "../.gitbook/includes/test-and-live-mode.md" %}
```
The path is relative to the file doing the include. The included file is plain markdown — no special wrapper needed.
### Variables (`.gitbook/vars.yaml`)
For values that appear on many pages — support emails, current API version date, environment URLs, version pins — define them once:
```yaml
support_email: support@lyra.app
api_version: "2026-04-01"
sandbox_host: api.sandbox.lyra.app
production_host: api.lyra.app
```
And reference them inline:
```markdown
The current pinned version is <code class="expression">space.vars.api_version</code>.
For help, contact <code class="expression">space.vars.support_email</code>.
```
When the value changes, you update one file, not 40.
### Card-tables for "choose your path" content
The home page of a multi-space site, the top of a how-to category, the API reference overview — anywhere the reader is making a choice between several options — uses card-tables instead of a bullet list:
```markdown
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody>
<tr>
<td><h3><i class="fa-bolt" style="color:$primary;">:bolt:</i></h3></td>
<td><strong>Quickstart</strong></td>
<td>Send your first feedback event in five minutes.</td>
<td><a href="getting-started/quickstart.md">quickstart</a></td>
</tr>
<tr>
<td><h3><i class="fa-diagram-project" style="color:$primary;">:diagram-project:</i></h3></td>
<td><strong>Data model</strong></td>
<td>The four core resources and how they relate.</td>
<td><a href="concepts/data-model.md">data-model</a></td>
</tr>
</tbody></table>
```
Yes the HTML is verbose. The visual result — icon + title + blurb + clickable card — is significantly better than any markdown alternative. Use them for the homepage's primary navigation cards.
### Columns for side-by-side layout
Two-up content (intro + callout, before/after, comparison) reads better in columns than stacked:
```markdown
{% columns %}
{% column width="50%" %}
The new dashboard is generally available across all plans. Existing customers
will see it on their next sign-in. The classic dashboard is reachable from the
account menu under "Settings → Use classic dashboard" until 2026-12-01.
{% endcolumn %}
{% column width="50%" %}
{% hint style="info" icon="lightbulb" %}
**Migrating?** The classic API endpoints are unchanged — only the UI is new.
{% endhint %}
{% endcolumn %}
{% endcolumns %}
```
Don't overuse columns — three-column layouts almost always look cramped, and on mobile both columns just stack vertically anyway. Two columns at 50/50 or 60/40 is the sweet spot.
### Conditional content for personas
When a docs site serves multiple personas (prospects, new customers, enterprise admins, partners), use `{% if visitor.claims... %}` to swap content rather than maintaining separate pages:
```markdown
{% if visitor.claims.unsigned.persona === "prospect" %}
{% hint style="success" icon="bag-shopping" %}
**Want to see this in action?** [Get a demo](https://lyra.app/demo) — we'll
walk you through with live data from a similar product.
{% endhint %}
{% endif %}
```
The conditions reference visitor claims that GitBook resolves at render time. Keep the conditional content additive (extra hints, links, callouts) — building entire alternate page bodies behind conditionals is hard to maintain and worse to read.
## Putting it together
A well-built docs site, in terms of block usage, looks like:
- **Space homepages** — default GitBook layout (TOC visible, default width) for normal docs; reach for `width: wide` + cover image only when the page is genuinely marketing/hero-style. Card-table for primary navigation; columns for intro pairs if useful.
- **Getting-started pages** — stepper for the walkthrough, tabs for multi-language code, hint blocks for warnings.
- **Concept pages** — Mermaid for state machines and flows, tables for comparisons, hints for caveats.
- **How-to pages** — stepper if there's a real walkthrough, prose otherwise; tabs only when you have genuinely parallel multi-language code.
- **Reference pages** — tables, OpenAPI auto-generation for endpoints, expandables for optional detail.
- **Changelog** — `{% updates %}` with tags and RSS, on a `width: wide` page with a brief intro paragraph.
- **API reference space** — overview README per resource, OpenAPI auto-gen for the operations themselves.
- **Cross-cutting** — `vars.yaml` for repeated literals, `.gitbook/includes/` for repeated paragraphs.
When the skill is generating a fresh docs site, walk through this list explicitly — for each piece of content being created, ask "is there a specialized block for this?" If yes, use it. If no, plain markdown.references/cross-space-links.md
# Cross-space links
Cross-space links are the connective tissue of a multi-space GitBook site — they're how the API reference points back to conceptual guides, how the changelog references the migration tutorial, how a card-table on the home page surfaces deep links into product docs. Without them, a multi-space site reads as a stack of disconnected manuals.
This reference covers: the URL pattern, the sentinel-and-resolve workflow for scaffolding, all the variants (space root, page, anchor, file), and a working resolution script.
## The URL pattern
Every cross-space link in markdown is a regular link to a `https://app.gitbook.com/s/<spaceId>/<path>` URL. GitBook's renderer resolves these at runtime — visitors land on the appropriate published URL whether the site is on a `*.gitbook.io` subdomain, a custom domain, or share-link visibility.
```markdown
[The whole space](https://app.gitbook.com/s/<spaceId>/)
[A specific page](https://app.gitbook.com/s/<spaceId>/concepts/authentication)
[A page with anchor](https://app.gitbook.com/s/<spaceId>/concepts/authentication#rotation)
```
The path after the space ID is the page's path inside the space, derived from the file structure (folder names + file slugs). Trailing slash works for the space root.
You'll also see a fully-qualified form in some content: `https://app.gitbook.com/o/<orgId>/s/<spaceId>/...`. Both work; the org-qualified form is slightly more robust against potential future URL changes but the shorter form is what GitBook itself emits in most places.
## When to use this vs. relative links
- **Within a space** — use plain relative paths: `[Quickstart](quickstart.md)`, `[Concepts](../concepts/data-model.md)`. These resolve at the markdown level, no IDs needed.
- **Across spaces** — use the `app.gitbook.com/s/<spaceId>/...` form. Relative paths can't cross space boundaries.
- **External URLs** — just the URL: `[Status](https://status.example.com)`.
## Linking to a space that already exists
The sentinel workflow below is for scaffolding — spaces that don't exist yet, so there's no ID to link to. If the target space already exists (you're adding a link from one already-published space into another), skip the sentinel step and just fetch the ID and path directly:
```bash
# 1. List spaces in the org, find the target by title
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
"https://api.gitbook.com/v1/orgs/$ORG_ID/spaces" | jq '.items[] | {id, title}'
# 2. List the target space's pages, find the target page's path
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
"https://api.gitbook.com/v1/spaces/$SPACE_ID/content/pages" | jq '.pages[] | {title, path}'
```
Each page object's `path` field (not its `id`) is what goes after the space ID in the URL — it's the same path segment shown in the page's `urls.app` field. Compose: `https://app.gitbook.com/s/<spaceId>/<path>`.
## The sentinel-and-resolve workflow
### Step 1 — agree on space keys during the structure plan
Each space in the plan gets a stable key — short, uppercase, descriptive. Use the same key everywhere:
```
guides → XSPACE_GUIDES
api-reference → XSPACE_API
changelog → XSPACE_CHANGELOG
```
These keys are *internal to the scaffolding step* and disappear once resolution runs.
### Step 2 — write cross-space links in markdown using sentinels
Anywhere a link crosses spaces:
```markdown
For the operational details — exact error codes, rate limits, header format — see
[Authentication in the API reference](https://app.gitbook.com/s/XSPACE_API/authentication).
Need a model of the data?
[Data model](https://app.gitbook.com/s/XSPACE_GUIDES/concepts/data-model) covers it.
```
The sentinels are valid URLs to non-existent spaces. Markdown parsers don't care, Git tracks them cleanly, and they grep with no false positives.
### Step 3 — after space creation, get real IDs and resolve
The API returns each space's `id` in the `POST /v1/orgs/{orgId}/spaces` response. Build a mapping and apply it:
```bash
# After space creation, you have a mapping like:
declare -A SPACE_IDS=(
[XSPACE_GUIDES]="V2euUoapjerbu1hCxVIv"
[XSPACE_API]="Si95BtOt1VRLWjT7A67V"
[XSPACE_CHANGELOG]="ErQsbFsgm6eg9BApdmPl"
)
# Apply substitutions across all markdown
for key in "${!SPACE_IDS[@]}"; do
find . -name '*.md' -not -path './.git/*' -print0 \
| xargs -0 sed -i "s|${key}|${SPACE_IDS[$key]}|g"
done
git add -u
git commit -m "Resolve cross-space link IDs"
git push
```
### Step 4 — keep a record
Before discarding the mapping, dump it to `cross-space-links.yaml` in the repo root:
```yaml
# Maps sentinel keys to GitBook space IDs.
# Used during initial scaffold to resolve XSPACE_<KEY> placeholders to real
# https://app.gitbook.com/s/<id>/ URLs across the markdown.
# After resolution there are no XSPACE_ placeholders left in the repo, but
# this file stays as a record of which key meant which space.
spaces:
XSPACE_GUIDES: V2euUoapjerbu1hCxVIv
XSPACE_API: Si95BtOt1VRLWjT7A67V
XSPACE_CHANGELOG: ErQsbFsgm6eg9BApdmPl
```
This makes the substitution reproducible if you ever need to add a new space later (you'd add a new `XSPACE_<NEW>` row to the mapping and re-run the resolver across files that reference it).
## Variants
### Link to a specific page in another space
The path after the space ID matches the file path inside that space, with `.md` dropped:
```markdown
[Webhooks reference](https://app.gitbook.com/s/XSPACE_API/webhooks)
[Verifying signatures](https://app.gitbook.com/s/XSPACE_API/webhooks/verifying-signatures)
```
### Link with an anchor (deep link to a heading)
Append `#<heading-slug>` like a regular markdown anchor:
```markdown
[Rate limit headers](https://app.gitbook.com/s/XSPACE_API/authentication#rate-limits)
```
GitBook generates heading slugs from heading text (lowercase, hyphenated). Confirm with the rendered page if a heading has unusual characters.
### Link to the space root (homepage)
Trailing slash, nothing else:
```markdown
[Open the API reference](https://app.gitbook.com/s/XSPACE_API/)
```
### Buttons, card-tables, and other rich blocks
Card-tables and inline buttons use the same URL pattern in their `href` attributes:
```html
<a href="https://app.gitbook.com/s/XSPACE_API/" class="button primary">API Reference</a>
```
Tables with `data-view="cards"` and a `content-ref` column accept the same URL in the column value.
### In SUMMARY.md
Cross-space links work in `SUMMARY.md` too — useful for cross-referencing related spaces from the table of contents itself. The same URL pattern applies, with **no `.md` suffix** on the target path:
```markdown
# Table of contents
* [Guides homepage](README.md)
* [Quickstart](getting-started/quickstart.md)
* [Concepts](concepts/README.md)
## See also
* [API Reference](https://app.gitbook.com/s/XSPACE_API/)
* [Changelog](https://app.gitbook.com/s/XSPACE_CHANGELOG/)
```
These render as outbound nav entries that route to the appropriate space. Resolution works the same way as for body content — the sentinel `XSPACE_<KEY>` gets replaced with the real space ID after creation. The resolver script in this file walks `*.md` files indiscriminately, so SUMMARY.md links are picked up automatically.
## When the substitution should be programmatic vs. manual
For 1–3 spaces with maybe a dozen cross-space links total, sed is fine. For larger sites with hundreds of cross-space links, write a small Python script:
```python
import re
import yaml
from pathlib import Path
mapping = yaml.safe_load(Path("cross-space-links.yaml").read_text())["spaces"]
for md in Path(".").rglob("*.md"):
if ".git" in md.parts:
continue
text = md.read_text()
new = text
for key, space_id in mapping.items():
new = new.replace(key, space_id)
if new != text:
md.write_text(new)
print(f"Resolved: {md}")
```
The script is idempotent — running it twice doesn't break anything, since after the first run there are no XSPACE_ placeholders left to substitute.
## Pages that move
When a page is moved or renamed, GitBook automatically creates a redirect from its old path, so links pointing at that path — relative or cross-space — keep resolving without any edits. Don't rewrite inbound links just because a page moved. For redirects outside that automatic coverage (e.g. restructuring done outside the GitBook UI), use `redirects:` in `.gitbook.yaml` (space-level) or the site redirects API (site-level, `POST/PUT /orgs/{orgId}/sites/{siteId}/redirects`).
## Common mistakes
- **Don't use `/spaces/<spaceId>/pages/<pageId>`.** This is not a valid GitBook link form, despite sometimes being suggested — cross-space links use the page's *path*, not its page ID, and the URL is always `https://app.gitbook.com/s/<spaceId>/<path>`.
- **Don't try to guess space IDs ahead of time.** They come from the API response after `POST /spaces`. Even a stable-looking ID format is opaque.
- **Don't use `<published-domain>/...` URLs across spaces** unless you've actually configured that domain. Sites without a custom domain live at `<orghostname>.gitbook.io/<sitehostname>/<sectionpath>/<spacepath>/<page>`, and that URL changes if anyone moves the site or section.
- **Don't strip the sentinel prefix from `XSPACE_<KEY>`.** Keep the `XSPACE_` prefix in case some future content has unrelated IDs that happen to match a key like `GUIDES`.
- **Don't forget to commit and push** after resolution — Git Sync needs the resolved content to render the links correctly.
- **Don't paraphrase to avoid a cross-space link.** A docs site that says "see the API reference" without linking is a worse docs site than one that links cleanly. Use the sentinel pattern and resolve.
## A note on what `write-docs` should know
The companion skill `write-docs` covers the markdown syntax for links in general. It should be aware that **cross-space links require resolved space IDs and won't work as relative paths**, and should produce them using the sentinel pattern when generating content for a multi-space site being scaffolded. The actual sentinel→ID resolution belongs to this skill (`configure-site`), since this skill is the one orchestrating space creation and has the IDs as soon as they exist.
If `write-docs` is being used standalone (without `configure-site` orchestrating) and the target space already exists, it doesn't need a sentinel at all — it can fetch the ID and path directly via the API (`GET /orgs/{orgId}/spaces`, `GET /spaces/{spaceId}/content/pages`), same as the "Linking to a space that already exists" section above. Sentinels are only needed when the target space doesn't exist yet.references/customization-recipes.md
# Customization recipes
Worked examples of `SiteCustomizationSettings` payloads for the most common branding scenarios. The single most useful reference here is **`example-site/customization.json`** — a real export from a production-style site. Read it before attempting any customization payload; it shows how all the nested fields fit together far better than any abstract example.
The pattern in every recipe: **GET current settings, modify the fields you care about with a small script, PUT the result back.** Don't construct payloads from scratch — required nested structures evolve and the GET response is your safest starting point.
```bash
ORG_ID=...
SITE_ID=...
GET_URL="https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/customization"
PUT_URL="$GET_URL"
CURRENT=$(curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" "$GET_URL")
NEW=$(echo "$CURRENT" | jq '<your modifications>')
curl -s -X PUT -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" -d "$NEW" "$PUT_URL"
```
## Scenario 0 — Ultimate defaults (apply once, then layer brand on top)
When you've created a site on the Ultimate plan, several feature flags should be turned **on by default** because they're what the user paid for. Apply this recipe first, then layer Scenario 1 or 2 on top for the actual brand.
```jq
.ai.mode = "assistant" # AI Assistant (Ultimate exclusive in full mode)
| .ai.suggestions = [] # populate with 3-5 starter prompts during structure design
| .pdf.enabled = true # PDF export per page
| .advancedCustomization.enabled = true # custom CSS/JS, custom fonts, etc.
| .trademark.enabled = false # hide "Powered by GitBook" footer (paid plans only)
| .pageActions.externalAI = true # "Open in ChatGPT" / "Open in Claude" buttons
| .pageActions.markdown = true # "Copy as markdown"
| .pageActions.mcp = true # "Connect via MCP"
| .git.showEditLink = true # "Edit on GitHub" / "Edit on GitLab" link on git-synced pages
```
Notes:
- The AI assistant needs `ai.suggestions` populated to be useful — empty starts cold. Three to five short questions visitors are likely to ask: *"How do I authenticate?"*, *"What are the rate limits?"*, *"How do I set up webhooks?"*. Gather these from the user during structure design.
- `trademark.enabled = false` is the typical Ultimate choice — paying for an Ultimate plan and leaving "Powered by GitBook" up is the unusual option, not the default.
- `pageActions` defaults to all-true on new Ultimate sites in some org configurations and all-false in others. Setting them explicitly removes the variance.
- Don't set Ultimate-only fields on a `basic` site — the API will accept the PUT and silently drop them, but the result looks broken. If the user is on free, skip this scenario entirely.
## Scenario 1 — Minimal brand pass
Goal: just change the site title, primary color, and favicon emoji. No paid features.
```jq
.title = "Acme Docs"
| .styling.theme = "clean"
| .styling.primaryColor = {"light": "#0E5BFF", "dark": "#5B8CFF"}
| .favicon = {"emoji": "📘"}
```
Notes:
- Colors are themed pairs — always supply both `light` and `dark` even if they're identical. Format is `#xxxxxx`.
- **Picking a dark-mode counterpart when the user gives only one color.** Brand colors usually look right on white but disappear (or burn) against a dark background. Two safe defaults: (a) ask the user for a dark variant and offer to derive one, or (b) lighten the brand color until it has roughly 4.5:1 contrast against `#0F172A` — typically pulling lightness up by 25–35% in HSL space. For a deep brand color like `#2D6A4F` (forest green), a paired light variant is around `#74C69D`; for `#534AB7` (deep indigo), around `#B2A5FF`. If unsure, set `light` and `dark` to the same value as a fallback — flat but legible.
- `favicon` accepts `{emoji: "📘"}` for an emoji or `{icon: {light: "...", dark: "..."}}` for hosted SVG/PNG URLs (themed).
- `styling.theme` choices: `clean`, `muted`, `bold`, `gradient`.
This works on free sites.
## Scenario 2 — Full brand with logos, fonts, semantic colors, footer
Goal: custom logo (light + dark), Inter font with IBM Plex Mono for code, semantic colors aligned with brand, branded footer with link groups and copyright. Requires Premium or Ultimate.
This recipe mirrors the example-site export — read `example-site/customization.json` for the exact shape of every nested field.
### A note on `header.logo.light` vs `header.logo.dark`
The schema field name describes **which mode the logo is shown in**, not the colour of the logo file. When a brand provides two logo files, ask explicitly which goes where if it isn't obvious — file names like `logo-dark.svg` ambiguously mean either "the dark-coloured logo" (intended for light backgrounds) or "the logo for dark mode" (a light-coloured logo). The schema is unambiguous:
- `header.logo.light` = the URL shown on light-mode pages → typically a dark-coloured logo
- `header.logo.dark` = the URL shown on dark-mode pages → typically a light-coloured logo
Same convention for `footer.logo` and `favicon.icon`.
```jq
.title = "Acme Platform Documentation"
| .styling.theme = "clean"
| .styling.primaryColor = {"light": "#534AB7", "dark": "#B2A5FF"}
| .styling.tint = {"color": {"light": "#F5F3FF", "dark": "#1E1B2E"}}
| .styling.infoColor = {"light": "#534AB7", "dark": "#534AB7"}
| .styling.warningColor = {"light": "#FE9A00", "dark": "#FE9A00"}
| .styling.dangerColor = {"light": "#FB2C36", "dark": "#FB2C36"}
| .styling.font = "Inter"
| .styling.monospaceFont = "IBMPlexMono"
| .styling.corners = "rounded"
| .styling.depth = "flat"
| .styling.icons = "duotone"
| .styling.sidebar = {"background": "filled", "list": "default"}
| .styling.search = "prominent"
| .header.logo = {"light": "https://acme.com/logo-light.svg", "dark": "https://acme.com/logo-dark.svg"}
| .header.links = [
{
"title": "Dashboard",
"style": "link",
"to": {"kind": "url", "url": "https://app.acme.com"},
"links": [],
"localizedTitle": {}
},
{
"title": "Get a demo",
"style": "button-primary",
"to": {"kind": "url", "url": "https://acme.com/demo"},
"links": [],
"localizedTitle": {}
}
]
| .footer.logo = {"light": "https://acme.com/logo-mono.svg", "dark": "https://acme.com/logo-mono-dark.svg"}
| .footer.copyright = "© 2026 Acme, Inc."
| .footer.groups = [
{
"title": "Product",
"localizedTitle": {},
"links": [
{"title": "Features", "localizedTitle": {}, "to": {"kind": "url", "url": "https://acme.com/features"}},
{"title": "Pricing", "localizedTitle": {}, "to": {"kind": "url", "url": "https://acme.com/pricing"}}
]
},
{
"title": "Company",
"localizedTitle": {},
"links": [
{"title": "About", "localizedTitle": {}, "to": {"kind": "url", "url": "https://acme.com/about"}},
{"title": "Careers", "localizedTitle": {}, "to": {"kind": "url", "url": "https://acme.com/careers"}}
]
}
]
| .favicon = {"icon": {"light": "https://acme.com/favicon.svg", "dark": "https://acme.com/favicon-dark.svg"}}
```
Notes:
- `font` and `monospaceFont` accept presets (`Inter`, `Roboto`, `IBMPlexMono`, `JetBrainsMono`, etc.) or full `CustomizationFontDefinitionInput` objects with hosted woff2 files. Use a preset unless the brand demands a specific custom font.
- Each header link needs `links: []` even if there's no submenu, and `localizedTitle: {}` even if there are no translations — the schema requires the keys.
- `header.preset = "default"` is also part of the response — leave it as-is.
- If a PUT with custom logos returns 400 or 403, the site is on a tier that doesn't permit custom logos. Either upgrade or fall back to the minimal recipe.
## Scenario 3 — Localized titles for a multi-language site
When auto-translation is enabled on a site, header and footer link titles can be localized so each language sees the right label. The `localizedTitle` map keys are language codes; English typically lives in the top-level `title` field, with translations in `localizedTitle`.
```jq
.footer.groups[0] = {
"title": "Products",
"localizedTitle": {"de": "Produkte", "es": "Productos", "fr": "Produits", "zh": "产品"},
"links": [
{
"title": "Payments",
"localizedTitle": {"de": "Zahlungen", "es": "Pagos", "fr": "Paiements", "zh": "支付"},
"to": {"kind": "space", "space": "<paymentsSpaceId>"}
}
]
}
```
The site title itself accepts `localizedTitle` too — set it for any user-facing string the site exposes per language.
## Scenario 4 — Conditional header links (gated by visitor claims)
Goal: show "Sign in" to logged-out visitors and "Sign out" to logged-in visitors. Both buttons live in the same `header.links` array, differentiated by their `condition` strings.
```jq
.header.links += [
{
"title": "Sign in",
"style": "button-secondary",
"to": {"kind": "url", "url": "https://app.acme.com/login"},
"links": [],
"localizedTitle": {},
"condition": "visitor.claims.unsigned.persona !== \"customer\""
},
{
"title": "Sign out",
"style": "button-secondary",
"to": {"kind": "url", "url": "https://app.acme.com/logout"},
"links": [],
"localizedTitle": {},
"condition": "visitor.claims.unsigned.persona === \"customer\""
}
]
```
The condition language is JS-like and references `visitor.claims` resolved at render time. Common patterns:
- **Persona switching**: `visitor.claims.unsigned.persona === "partner"`
- **Plan gating**: `visitor.claims.signed.plan === "enterprise"`
- **Logged-in check**: `visitor.claims.signed`
Use `unsigned` claims for hints from URL params (e.g. `?visitor.persona=prospect`); use `signed` claims for verified ones from authenticated sessions.
## Scenario 5 — Link to a space or specific page
Header and footer links can target a whole space or a specific page in a space, not just URLs:
```jq
# Link a footer item to the changelog space
.footer.groups[0].links += [{
"title": "Changelog",
"localizedTitle": {},
"to": {"kind": "space", "space": "<changelogSpaceId>"}
}]
# Link to a specific page inside a space
.footer.groups[0].links += [{
"title": "Latest release",
"localizedTitle": {},
"to": {"kind": "page", "page": "<pageId>", "space": "<spaceId>"}
}]
```
Get space and page IDs from the structure response or by looking them up in the space API.
## Scenario 6 — Theme mode default and toggle
```jq
.themes.default = "system" # follow the visitor's OS preference; alternatives: "light", "dark"
| .themes.toggeable = true # visitors can override
```
### True-black dark mode via `styling.tint`
For a dark mode that's actually dark — a true `#000000` page background instead of GitBook's default soft-dark-gray (`~#0F1419`) — set `styling.tint.color.dark` to pure black. The rest of the dark palette (cards, sidebars, code blocks) is calculated against the tint, so this anchors everything else to true black.
```jq
.styling.tint = {
"color": {
"light": "#FFFFFF", # pure white in light mode (rare; default off-white usually reads better)
"dark": "#000000" # true black in dark mode — OLED-friendly, high-contrast
}
}
```
Pair with a dark-by-default theme for the cleanest effect:
```jq
.themes.default = "dark"
| .themes.toggeable = true
| .styling.tint.color.dark = "#000000"
```
This is the single highest-impact knob for dark-mode aesthetics — without it, dark mode looks washed out compared to the user's brand expectations.
## Scenario 7 — AI assistant and page actions
```jq
.ai.mode = "assistant"
| .ai.suggestions = [
"How do I authenticate with the API?",
"What pricing tier supports SSO?",
"How do I set up webhooks?"
]
| .pageActions.externalAI = true # show "Open in ChatGPT" menu
| .pageActions.markdown = true # show "Copy as markdown"
| .pageActions.mcp = true # show "Connect via MCP"
```
`ai.mode` choices: `none`, `search` (search-only), `assistant` (full chat). Up to 5 suggestions, each 3–64 chars. `pageActions` are independent toggles — leave any unset to keep the current value.
## Scenario 8 — Social accounts in header / footer
```jq
.socialAccounts = [
{"platform": "twitter", "handle": "acme", "display": {"footer": true, "header": false}},
{"platform": "github", "handle": "acmeinc/docs", "display": {"footer": true, "header": true}},
{"platform": "linkedin", "handle": "company/acme", "display": {"footer": true, "header": false}}
]
```
`platform` choices include `twitter`, `github`, `linkedin`, `youtube`, `discord`, `facebook`, `instagram`, `mastodon`, `bluesky`. The `handle` format is platform-specific — twitter is just the username, github accepts `user` or `user/repo`, linkedin uses `company/<slug>`.
## Scenario 9 — Per-site-space override
When a multi-space site has one space that needs different branding (e.g. an internal docs space with no AI and a different logo), set the overrides on the site-space:
```bash
SITE_SPACE_ID=...
SS_GET_URL="https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/site-spaces/$SITE_SPACE_ID/customization"
SS_CURRENT=$(curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" "$SS_GET_URL")
# Disable AI just for this space
SS_NEW=$(echo "$SS_CURRENT" | jq '.ai.mode = "none"')
curl -s -X PUT -H "Authorization: Bearer $GITBOOK_TOKEN" \
-H "Content-Type: application/json" -d "$SS_NEW" "$SS_GET_URL"
```
To reset the override and fall back to site-wide settings:
```bash
curl -s -X DELETE -H "Authorization: Bearer $GITBOOK_TOKEN" \
"https://api.gitbook.com/v1/orgs/$ORG_ID/sites/$SITE_ID/site-spaces/$SITE_SPACE_ID/customization"
```
## Field cheatsheet
A quick reference for the customization fields most often touched. For the full nested schema, look at the response of a GET — every field present there is settable. The bundled `example-site/customization.json` shows realistic values for nearly all of them.
| Field path | Purpose | Notes |
|---|---|---|
| `title` / `localizedTitle` | Site title in header | 2–128 chars; localizedTitle is `{en, fr, ...}` |
| `internationalization.locale` | Default locale | **Deprecated** — kept for read compatibility only |
| `styling.theme` | Visual preset | `clean | muted | bold | gradient` |
| `styling.primaryColor` | Brand color | Themed `{light, dark}` |
| `styling.tint` | Subtle site-wide tint applied to text/icons/UI | `{color: {light, dark}}` — does **not** affect links/buttons (those use primaryColor) |
| `styling.infoColor`, `successColor`, `warningColor`, `dangerColor` | Semantic colors | Themed pairs (Premium for full control) |
| `styling.font` | Body font | Preset name or font definition |
| `styling.monospaceFont` | Code font | Preset name or font definition |
| `styling.corners` | Border radius style | `straight | rounded | circular` |
| `styling.depth` | Shadow style | `subtle | flat` |
| `styling.icons` | Icon weight | `regular | solid | duotone | light | thin` |
| `styling.background` | Page background | **Required by API** despite older docs marking it deprecated. Set to `"plain"` on new sites, or echo whatever GET returns on round-trip updates. POSTing without it returns `422 expected "background" to be defined`. |
| `styling.links` | Link style | `default | accent` |
| `styling.sidebar.background` | Sidebar bg | `default | filled` |
| `styling.sidebar.list` | Sidebar list style | `default | pill | line` |
| `styling.codeTheme.default` | Default code-block theme | `{light, dark}` strings like `default-light` |
| `styling.codeTheme.openapi` | OpenAPI block theme | Same shape as `.default` |
| `styling.search` | Search bar prominence | `prominent | subtle` |
| `favicon` | Site favicon | `{emoji}` or `{icon: {light, dark}}` |
| `header.preset` | Header layout preset | **Deprecated** — use `styling.theme` instead |
| `header.logo` | Themed logos in header | `{light: <url-for-light-mode>, dark: <url-for-dark-mode>}` (Premium). Schema field name = which mode shows it, not the colour of the file. |
| `header.backgroundColor`, `header.linkColor` | Header colours | **Deprecated** — use theming colours |
| `header.links[]` | Top-nav items | `{title, style, to, links, localizedTitle, condition?}`. **`links: []` is required** even when the item has no sub-menu — POSTing without it returns `422 expected "links" to be defined`. |
| `header.primaryLink` | Where the logo links to | A `ContentRef` (see Content references section) |
| `footer.logo` | Themed footer logo | Same shape as `header.logo` (Premium) |
| `footer.groups[]` | Footer link groups | Each: `{title, localizedTitle, links}` |
| `footer.copyright` | Copyright line | Up to 300 chars |
| `themes.default` | Default mode | `light | dark | system` |
| `themes.toggeable` | Allow visitor toggle | bool |
| `ai.mode` | AI feature level | `none | search | assistant` |
| `ai.suggestions` | Starter prompts | Up to 5, 3–64 chars each |
| `pdf.enabled` | PDF export button | bool |
| `feedback.enabled` | Page feedback widget | bool |
| `pagination.enabled` | Prev/next page links | bool |
| `pageActions.externalAI` | "Open in ChatGPT" | bool |
| `pageActions.markdown` | "Copy as markdown" | bool |
| `pageActions.mcp` | "Connect via MCP" | bool |
| `externalLinks.target` | Outbound link target | `self | blank` |
| `trademark.enabled` | "Powered by GitBook" | bool — paid plans can disable |
| `socialPreview.url` | OG image URL | string URL |
| `socialAccounts[]` | Social icons | `{platform, handle, display: {footer, header}}` |
| `git.showEditLink` | "Edit on GitHub" link | bool |
| `insights.trackingCookie` | GitBook analytics cookie | bool |
| `advancedCustomization.enabled` | Allow custom CSS/JS | bool (Enterprise) |
| `privacyPolicy.url` | Footer privacy link | string URL |
| `announcement` | Site-wide banner | `{enabled, message, link?, style: info|warning|danger|success}` |
## Things that look like customization but aren't
A few related settings live elsewhere in the API:
- **Custom domain** — set on the *site* itself (`Site.hostname`), not in customization. PATCH the site object.
- **Site visibility** (public, share-link, etc.) — also on the site object.
- **Per-page layout** (cover, table of contents visibility) — set in page frontmatter; see the `write-docs` skill.
- **Auto-translation enablement** — UI-only today (Site → Sections → Translations).
Don't try to put any of these in the customization payload; the API will reject them.references/example-site/.nojekyll
references/example-site/changelog/.gitbook/tags.yaml
- tag: payments
label: Payments
icon: credit-card
- tag: identity
label: Identity
icon: id-card
- tag: connect
label: Connect
icon: circles-overlap
- tag: platform
label: Platform
icon: layer-group
references/example-site/changelog/.gitbook/vars.yaml
api_live: https://api.evolve.com
dashboard_live: https://dashboard.evolve.com
docs_root: https://docs.evolve.com
status_page: https://status.evolve.com
references/example-site/changelog/README.md
---
description: Product updates across Payments, Identity, Connect, and the platform.
icon: clock-rotate-left
layout:
width: wide
title:
visible: true
description:
visible: true
tableOfContents:
visible: false
outline:
visible: true
pagination:
visible: true
metadata:
visible: true
tags:
visible: true
---
# Changelog
{% columns %}
{% column width="50%" %}
Everything we've shipped across Evolve in the last six months. Filter by product tag using the controls in the top right of the timeline.
New entries land roughly weekly. Subscribe via [RSS](https://gitbook.com) or follow [@evolvepay](https://gitbook.com) for major releases.
{% endcolumn %}
{% column width="50%" %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This page is a single **Updates block** — a timeline of dated entries with **tags** (defined in `.gitbook/tags.yaml`). The four tags here are `payments`, `identity`, `connect`, `platform`; multi-tagged entries get all their tags. **RSS** is auto-generated for the page.
{% endhint %}
{% endcolumn %}
{% endcolumns %}
{% updates format="full" %}
{% update date="2026-04-29" tags="payments" %}
## Smart routing v2
Per-network success-rate optimization is now generally available on Growth and Enterprise. Most teams see a 1–3% lift in approval rate on cards that previously routed through a single acquirer.
The new model picks acquirers based on per-card-type historical approval rates, network token availability, and transaction shape — and falls back to a secondary acquirer on soft declines without the customer noticing.
Smart routing is on by default for accounts where it's eligible. To opt out or customize routing rules, see **Settings → Routing**.
{% endupdate %}
{% update date="2026-04-22" tags="identity" %}
## Selfie liveness 2.0
Passive liveness — no head turns, no smile-on-command, no spelling out numbers. The customer just holds the camera in front of their face for about three seconds.
Completion rates are up \~12% in our beta cohort, with a small drop in fraud-detection rate that we've offset by tighter document-tampering checks. Active rollout to all accounts over the next two weeks.
{% endupdate %}
{% update date="2026-04-18" tags="identity" %}
## Plaid instant for businesses
Bank account verification on connected accounts now uses Plaid's business-bank integration where supported. Same one-tap UX as the consumer flow, with verified business owner names checked against the connected account's beneficial owners.
{% endupdate %}
{% update date="2026-04-12" tags="connect" %}
## Per-seller subdomains
Enterprise platforms can now route per-seller checkout to a custom subdomain (`acme.checkout.evolve.com`). Useful for marketplaces with strong-brand sellers who want their checkout URL to match their brand. Configure in **Connect → Branding → Per-seller overrides**.
{% endupdate %}
{% update date="2026-04-05" tags="platform" %}
## Audit log export to S3 and GCS
Push your audit log to S3 or Google Cloud Storage on a schedule. Useful for SIEM ingestion (Splunk, Datadog) and long-term retention beyond our 7-year built-in window.
{% endupdate %}
{% update date="2026-04-01" tags="payments,platform" %}
## Payments API v3-beta available
A preview of the next major version of the Payments API. Highlights:
* `Charge` is renamed to `Payment` (`pay_*` IDs).
* `capture: true|false` is replaced by a `capture_method` enum: `automatic`, `manual`, `automatic_async`.
* Authorization window extended from 7 to 30 days.
* Multi-currency capture — authorize in one currency, capture in another.
v3-beta is preview-only. Don't run production traffic on it without coordinating with your account team. v2 stays the stable default; v3-beta will graduate when we've completed the beta-customer cohort.
The variant dropdown in [Developers](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api/) lets you flip between v1, v2, and v3 to compare.
{% endupdate %}
{% update date="2026-03-26" tags="connect" %}
## Application fees report improvements
The platform-revenue report under **Reports → Application fees** now supports per-seller cohort breakdowns and CSV export with full metadata. Useful for daily revenue reconciliation.
{% endupdate %}
{% update date="2026-03-21" tags="payments" %}
## Same-day payouts (Enterprise)
US Enterprise accounts can now opt in to same-day payouts for an additional 0.4% per transfer. Funds land in your bank account within hours rather than the next business day. Configure in **Settings → Payouts**.
For platforms running Connect, same-day payouts are also available per-seller.
{% endupdate %}
{% update date="2026-03-14" tags="identity" %}
## KYB in 12 new countries
Business verification now covers Argentina, Brazil, Chile, Colombia, India, Indonesia, Malaysia, Mexico, Peru, Philippines, Thailand, Vietnam — all with national-register integrations and local sanctions screening.
{% endupdate %}
{% update date="2026-03-07" tags="platform" %}
## Datadog APM integration
Drop-in OpenTelemetry support in all four official SDKs. Trace spans from your application code through Evolve's edge in your Datadog APM views. Off by default; enable with `EVOLVE_OTEL_ENABLED=true`.
{% endupdate %}
{% update date="2026-03-01" tags="payments" %}
## Disputes API generally available
Programmatic evidence submission is now GA, replacing the legacy CSV upload. The new API supports up to 10 file attachments per dispute, structured evidence fields per reason code, and pre-emptive refunds via Verifi/Ethoca alerts (Enterprise).
{% endupdate %}
{% update date="2026-02-26" tags="connect" %}
## Bulk seller onboarding via CSV
Migrating from another platform? Upload a CSV of seller info under **Connect → Seller bulk import**. Evolve generates per-seller hosted onboarding URLs you can email out from your side.
{% endupdate %}
{% update date="2026-02-19" tags="identity" %}
## Adverse media screening
Watchlist screening now includes an adverse-media category — news mentions tying a verified identity to specific risk topics (financial crime, terrorism, sanctions evasion). Available on Enterprise. Most teams treat adverse-media matches as manual-review triggers rather than automatic rejection — the false-positive rate is non-trivial.
{% endupdate %}
{% update date="2026-02-14" tags="platform" %}
## SCIM provisioning
Auto-provision team members from your IdP (Okta, Microsoft Entra ID, OneLogin, others). Configure under **Settings → Security → SCIM**. Available on Growth and Enterprise.
{% endupdate %}
{% update date="2026-02-08" tags="payments" %}
## Refund window extended on Enterprise
Enterprise accounts can now refund a charge up to 180 days after capture, up from 90 on Growth. Useful for slow-fulfillment goods and B2B with longer reconciliation cycles.
{% endupdate %}
{% update date="2026-02-03" tags="connect" %}
## Failover for international acquirers
Enterprise platforms with sellers in multiple regions now have failover support for the EU and UK acquirers as well as US. Configure per-region routing under **Settings → Routing → Failover**.
{% endupdate %}
{% update date="2026-01-31" tags="platform" %}
## New dashboard search
The dashboard's global search now returns ranked results across customers, charges, payouts, verification sessions, and connected accounts, with relevance based on your team's recent searches and your role's data scope.
{% endupdate %}
{% update date="2026-01-25" tags="identity" %}
## Re-verification webhook improvements
The `verification_session.reverification_required` event now includes the trigger reason (`chargeback`, `large_transaction`, `address_change`, `scheduled`) so your handler can route to different downstream flows.
{% endupdate %}
{% update date="2026-01-22" tags="payments" %}
## Multi-currency capture preview
Authorize in one currency, capture in another at the daily wholesale rate plus your configured FX margin. Available in v3-beta. Useful for marketplaces with international sellers — authorize in the buyer's currency, pay out in the seller's.
{% endupdate %}
{% update date="2026-01-17" tags="connect" %}
## Embedded checkout customization
Enterprise platforms can now customize the embedded checkout's font, layout, and CSS. Per-seller font and CSS overrides also supported. Configure under **Connect → Branding**.
{% endupdate %}
{% update date="2026-01-12" tags="platform" %}
## SDK v2 — Node, Python, Go, Ruby
The 2.x lines of all four official SDKs are now stable, tracking the v2 Payments API. Highlights:
* Auto-generated idempotency keys on every write call (configurable).
* Auto-retries with exponential backoff on transient errors.
* Built-in webhook signature verification.
* Native async support in Node and Python.
* Strict module boundaries in Go (per-resource packages).
The 1.x lines are still maintained for the v1 Payments API; they reach end-of-life when v1 sunsets on 2026-12-31. See [Developers / SDKs](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/sdks).
{% endupdate %}
{% update date="2026-01-08" tags="identity" %}
## Document support in 12 new countries
Driver's-license and national-ID-card support added for Bangladesh, Egypt, Ghana, Kenya, Morocco, Nigeria, Pakistan, Saudi Arabia, South Africa, Tunisia, UAE, and Vietnam. Brings supported document countries to 195.
{% endupdate %}
{% update date="2026-01-05" tags="payments" %}
## Improved decline-code messages
The `message` field on declined charges is now consistent across all SDKs (previously varied by SDK locale). The `code` and `decline_code` fields are unchanged — build your error handling around those rather than the message text.
{% endupdate %}
{% update date="2025-12-19" tags="platform" %}
## Year-end maintenance window
A scheduled 30-minute maintenance window on December 23, 02:00 UTC. No expected downtime; a brief reduction in async processing capacity. See [<code class="expression">space.vars.status_page</code>](https://gitbook.com) for live updates.
{% endupdate %}
{% update date="2025-12-15" tags="connect" %}
## On-demand payouts (Enterprise)
Enterprise platforms can now offer their sellers an on-demand payout button — instant cash to the seller's bank for a 1% fee. Configure in **Connect → Settings → Instant payouts**.
{% endupdate %}
{% update date="2025-12-09" tags="payments" %}
## Disputes API beta
Beta release of the new disputes API ahead of GA in March. Sign up for the beta cohort under **Settings → Beta features**.
{% endupdate %}
{% update date="2025-12-04" tags="identity" %}
## Manual review queue improvements
The manual review queue now supports bulk actions, saved filters, and per-reviewer assignment. Useful for compliance teams handling more than 50 reviews/week.
{% endupdate %}
{% update date="2025-12-01" tags="platform" %}
## SOC 2 Type II report — 2025
Our 2025 SOC 2 Type II audit completed with no exceptions. Report is available under NDA from your account team. ISO 27001 recertification is also complete; PCI-DSS Level 1 attestation expected mid-January.
{% endupdate %}
{% update date="2025-11-26" tags="connect" %}
## Application fees report
A new daily/weekly/monthly aggregate report under **Reports → Application fees**, showing platform revenue, take rate, and per-seller fee earnings. Exportable to CSV and the standard scheduled-export destinations.
{% endupdate %}
{% update date="2025-11-21" tags="payments" %}
## 3-D Secure 2.2 support
Updated 3DS-2 implementation to spec version 2.2 across all card networks. Better browser-fingerprinting accuracy improves the frictionless-flow rate by \~5%.
{% endupdate %}
{% update date="2025-11-15" tags="identity" %}
## Bank verification fallback improvements
When Plaid instant fails (e.g. unsupported bank), the flow now falls back to micro-deposits seamlessly within the same hosted session — no separate session creation needed.
{% endupdate %}
{% update date="2025-11-10" tags="platform" %}
## SDK telemetry
Anonymous SDK usage telemetry (version, request shape, error class) is now collected by default to help us catch regressions. Disable with `EVOLVE_TELEMETRY=off`. No request bodies, no PII, no card data.
{% endupdate %}
{% update date="2025-11-04" tags="payments" %}
## Saved payment methods improvements
The `setup_future_usage` field on Checkout sessions now supports `on_session` (one-tap reuse) in addition to `off_session` (recurring). Better mandate handling reduces friction on the network's side, with about a 0.7% lift in repeat-purchase approval rates.
{% endupdate %}
{% endupdates %}
## Older releases
For releases before November 2025, the [archive](https://gitbook.com) covers the prior 24 months. Major releases are also tagged on our [GitHub repo](https://github.com/GitbookIO/evolve-demo) for the SDK side.
For platform incidents and maintenance windows, the [status page](https://gitbook.com) maintains a separate incident log.
references/example-site/changelog/SUMMARY.md
# Table of contents
* [Changelog](README.md)
references/example-site/CLAUDE.md
## GitBook Docs
Use the `gitbook-documentation` MCP connector to look up documentation before formatting
or writing GitBook content. Always check the docs for the correct markdown
syntax, callout block format, and page structure conventions.
references/example-site/connections/blog/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Evolve Blog</title>
<meta name="description" content="Engineering and product posts from the Evolve team.">
</head>
<body>
<header>
<h1>Evolve Blog</h1>
<p>Engineering and product posts from the team building Evolve.</p>
</header>
<main>
<ul>
<li><a href="smart-routing-v2.html">Smart routing v2: how we lifted approval rates 1–3%</a> — 2026-04-29</li>
<li><a href="why-identity-is-its-own-product.html">Why we made Identity its own product, not a Payments feature</a> — 2026-03-14</li>
<li><a href="connect-unified-accounts.html">Killing Express, Standard, and Custom: the unified Connect account model</a> — 2026-02-08</li>
<li><a href="same-day-payouts-tradeoffs.html">Same-day payouts: when 0.4% is worth it (and when it isn't)</a> — 2026-01-22</li>
<li><a href="building-for-ai-agents.html">Building docs and APIs for AI agents, not just humans</a> — 2025-12-19</li>
</ul>
</main>
</body>
</html>
references/example-site/connections/blog/same-day-payouts-tradeoffs.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Same-day payouts: when 0.4% is worth it (and when it isn't) — Evolve Blog</title>
<meta name="description" content="A practical guide to evaluating whether same-day payouts make sense for your business — with the math behind the decision.">
<meta name="author" content="Priya Shah, Product Manager, Payments">
<meta property="article:published_time" content="2026-01-22">
<meta name="keywords" content="payouts, same-day, cash flow, enterprise">
</head>
<body>
<article>
<header>
<h1>Same-day payouts: when 0.4% is worth it (and when it isn't)</h1>
<p>By Priya Shah, Product Manager, Payments — January 22, 2026</p>
</header>
<p>Same-day payouts went GA on Enterprise plans this month: hit a button, your settled balance lands in your bank within hours instead of next business day, for a 0.4% premium per transfer. The customers who pre-ordered the feature broke down into two clear groups. One group was platforms with seller-facing payout SLAs — they'd been paying for early-payout APIs from third-party providers and saving money switching to native. The other was finance teams running tight on working capital who could put 24 hours of float to better use than the fee cost.</p>
<p>For most teams, the standard next-day payout is still the right default. The math only flips in your favor when working-capital cost exceeds 0.4% on the relevant horizon — high-velocity businesses, platforms passing payouts to sellers, or teams in a brief cash crunch. Configure under <strong>Settings → Payouts</strong> if you want to experiment per-transfer rather than always-on.</p>
</article>
</body>
</html>
references/example-site/connections/community/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Evolve Community Forum</title>
<meta name="description" content="Discussion and Q&A from the Evolve community — peer help, integration patterns, and product feedback.">
</head>
<body>
<header>
<h1>Evolve Community Forum</h1>
<p>Discussion and Q&A from developers, ops teams, and platform builders using Evolve.</p>
</header>
<main>
<h2>Recent threads</h2>
<ul>
<li><a href="3ds-on-subscription-renewals.html">Best way to handle 3-D Secure on subscription renewals?</a> — 14 replies</li>
<li><a href="migrating-stripe-customers.html">Migrating Stripe Customers to Evolve — gotchas to watch for?</a> — 22 replies</li>
<li><a href="webhook-retries-backoff.html">Webhook retries — what's the actual backoff curve?</a> — 9 replies</li>
<li><a href="connect-payout-stuck.html">Per-seller payout stuck in "pending" — what now?</a> — 6 replies</li>
<li><a href="decline-codes-vs-card-decline-codes.html">Difference between decline_code and code on a failed charge?</a> — 11 replies</li>
<li><a href="kyb-beneficial-ownership-edge-cases.html">KYB beneficial-ownership: handling LLCs owned by trusts?</a> — 8 replies</li>
</ul>
</main>
</body>
</html>
references/example-site/connections/community/webhook-retries-backoff.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webhook retries — what's the actual backoff curve? — Evolve Community</title>
<meta name="description" content="Discussion on Evolve's webhook retry schedule and the backoff curve — including how to handle dead-letter scenarios.">
<meta name="author" content="kev_d">
<meta property="article:published_time" content="2026-04-04">
<meta name="keywords" content="webhooks, retries, backoff, reliability">
</head>
<body>
<article>
<header>
<h1>Webhook retries — what's the actual backoff curve?</h1>
<p>Posted by <strong>kev_d</strong> — April 4, 2026 — 9 replies</p>
</header>
<section class="post original">
<p>Docs say "exponential backoff" but I want to know the exact schedule so I can tune our endpoint's idempotency window. Anyone got the numbers?</p>
</section>
<section class="post reply accepted">
<header><strong>akshaya — Evolve team</strong> — April 4, 2026 — accepted answer</header>
<p>Schedule for the standard tier: 1m, 5m, 30m, 2h, 12h, 1d, 3d, 7d. After 7 days the event moves to the dead-letter queue and we email your account's webhook contact. Total of 8 attempts over a 7-day window. Enterprise can configure custom curves.</p>
<p>Tune your idempotency window to at least 7 days — we've seen integrations where a delivery success on attempt 8 conflicted with a manual retry someone did at hour 6, leading to double-processing.</p>
</section>
</article>
</body>
</html>
references/example-site/connections/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Evolve — External sources</title>
<meta name="description" content="External content sources for Evolve: blog, community forum, and YouTube channel.">
</head>
<body>
<header>
<h1>Evolve — External sources</h1>
<p>External content surfaces that the Evolve Assistant pulls from alongside the docs.</p>
</header>
<main>
<ul>
<li><a href="blog/index.html">Blog</a> — engineering and product posts</li>
<li><a href="community/index.html">Community forum</a> — discussion and Q&A</li>
<li><a href="youtube/index.html">YouTube channel</a> — walkthroughs and deep-dives</li>
</ul>
</main>
</body>
</html>
references/example-site/connections/youtube/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Evolve YouTube Channel</title>
<meta name="description" content="Walkthroughs, deep-dives, and monthly product updates from the Evolve team.">
</head>
<body>
<header>
<h1>Evolve on YouTube</h1>
<p>Walkthroughs, deep-dives, and monthly product updates. Subscribe for new releases.</p>
</header>
<main>
<h2>Recent uploads</h2>
<ul>
<li><a href="set-up-connect-in-10-minutes.html">Set up a marketplace with Connect in 10 minutes</a> — 9:42</li>
<li><a href="chargebacks-at-scale.html">Handling chargebacks at scale: a Connect playbook</a> — 14:18</li>
<li><a href="webhooks-deep-dive.html">Webhooks deep-dive: signatures, retries, idempotency</a> — 17:55</li>
<li><a href="migrate-from-stripe-walkthrough.html">Migrate from Stripe to Evolve in a weekend (live build)</a> — 28:31</li>
<li><a href="kyb-end-to-end.html">KYB end-to-end: business verification with beneficial ownership</a> — 12:07</li>
<li><a href="smart-routing-explained.html">Smart routing v2 explained — what's actually happening behind the scenes</a> — 11:24</li>
</ul>
</main>
</body>
</html>
references/example-site/connections/youtube/webhooks-deep-dive.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webhooks deep-dive: signatures, retries, idempotency — Evolve YouTube</title>
<meta name="description" content="The full webhook story: signature verification across SDKs, the retry schedule, idempotency window sizing, and dead-letter handling.">
<meta name="author" content="Evolve">
<meta property="video:duration" content="17:55">
<meta property="og:type" content="video.other">
<meta property="article:published_time" content="2026-02-26">
<meta name="keywords" content="webhooks, signatures, idempotency, reliability">
</head>
<body>
<article>
<header>
<h1>Webhooks deep-dive: signatures, retries, idempotency</h1>
<p>Channel: <strong>Evolve</strong> · Duration: 17:55 · Published: February 26, 2026 · 24,701 views</p>
</header>
<section class="description">
<p>The full webhook story for production-ready integrations. Signature verification across all four SDKs, the retry schedule (1m, 5m, 30m, 2h, 12h, 1d, 3d, 7d), idempotency window sizing, dead-letter handling, and the one mistake that almost everyone makes the first time.</p>
</section>
<section class="chapters">
<h2>Chapters</h2>
<ul>
<li>0:00 — What webhooks are for (and what they aren't)</li>
<li>1:45 — Signature verification: HMAC-SHA-256, replay protection</li>
<li>5:30 — The retry schedule and why it ramps the way it does</li>
<li>9:12 — Idempotency: keys, windows, and de-dup at scale</li>
<li>12:48 — Dead-letter handling and account-contact emails</li>
<li>15:20 — The one mistake everyone makes (parsing the message field for branching)</li>
</ul>
</section>
<section class="transcript">
<h2>Transcript highlights</h2>
<p>"…tune your idempotency window to at least 7 days. Our last retry attempt is at the 7-day mark. If your window is shorter than that, a successful late delivery can collide with a manual retry from your ops team and you end up double-processing…"</p>
<p>"…signature verification isn't optional. Even if you whitelist our IPs, IPs change. The signature is a 30-second per-customer engineering task that catches a real category of attacks…"</p>
</section>
</article>
</body>
</html>
references/example-site/customization.json
{
"updatedAt": "2026-05-04T10:08:47.000Z",
"title": "Evolve Platform | GitBook Enterprise Demo",
"localizedTitle": {},
"internationalization": {
"locale": "en"
},
"styling": {
"theme": "clean",
"primaryColor": {
"dark": "#B2A5FF",
"light": "#534AB7"
},
"infoColor": {
"dark": "#534AB7",
"light": "#534AB7"
},
"successColor": {
"dark": "#777777",
"light": "#BBBBBB"
},
"warningColor": {
"dark": "#FE9A00",
"light": "#FE9A00"
},
"dangerColor": {
"dark": "#FB2C36",
"light": "#FB2C36"
},
"corners": "circular",
"depth": "flat",
"links": "default",
"font": "Inter",
"monospaceFont": "IBMPlexMono",
"icons": "duotone",
"background": "plain",
"sidebar": {
"background": "filled",
"list": "default"
},
"codeTheme": {
"default": {
"light": "default-light",
"dark": "default-dark"
},
"openapi": {
"light": "default-light",
"dark": "default-dark"
}
},
"search": "prominent"
},
"favicon": {
"icon": {
"light": "https://896710490-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/organizations%2F2DnmWBpytIOUKeXExonU%2Fsites%2Fsite_kvXZm%2Ficon%2FR1BwDwvyRugXoT4EvNOX%2FIcon.svg?alt=media&token=b2ea31fd-c832-4d41-a5eb-a93ab8221aea",
"dark": "https://896710490-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/organizations%2F2DnmWBpytIOUKeXExonU%2Fsites%2Fsite_kvXZm%2Ficon%2F0IAA4rMnj04oipgchFOo%2FIcon.svg?alt=media&token=2034d190-bdc5-4c9b-9658-2a02df6428aa"
}
},
"header": {
"preset": "default",
"logo": {
"light": "https://896710490-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/organizations%2F2DnmWBpytIOUKeXExonU%2Fsites%2Fsite_kvXZm%2Flogo%2FjG5rZJWsgbuAPwFOB7bB%2FEvolve%20Light.svg?alt=media&token=3c1fa28b-e914-470e-9c18-96496cd50eff",
"dark": "https://896710490-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/organizations%2F2DnmWBpytIOUKeXExonU%2Fsites%2Fsite_kvXZm%2Flogo%2FE6xI9KkOJ3NH9HDKbLuF%2FEvolve%20Dark.png?alt=media&token=3b97d3ef-a7c3-4e35-b661-cbdbe60ce1c8"
},
"links": [
{
"to": null,
"links": [
{
"to": {
"url": "https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect",
"kind": "url"
},
"title": "Prospect",
"localizedTitle": {}
},
{
"to": {
"url": "https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&visitor.plan=starter",
"kind": "url"
},
"title": "New user",
"localizedTitle": {}
},
{
"to": {
"url": "https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth",
"kind": "url"
},
"title": "Migrator",
"localizedTitle": {}
},
{
"to": {
"url": "https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise",
"kind": "url"
},
"title": "Partner",
"localizedTitle": {}
},
{
"to": {
"url": "https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=",
"kind": "url"
},
"title": "↩ Reset",
"localizedTitle": {}
}
],
"style": "link",
"title": "View as...",
"localizedTitle": {}
},
{
"to": {
"url": "https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise",
"kind": "url"
},
"links": [],
"style": "button-secondary",
"title": "Sign in",
"condition": "visitor.claims.unsigned.persona !== \"partner\"",
"localizedTitle": {}
},
{
"to": {
"url": "https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=",
"kind": "url"
},
"links": [],
"style": "button-secondary",
"title": "Sign out",
"condition": "visitor.claims.unsigned.persona === \"partner\"",
"localizedTitle": {}
},
{
"to": {
"url": "https://gitbook.com/enterprise",
"kind": "url"
},
"links": [],
"style": "button-primary",
"title": "GitBook Enterprise",
"localizedTitle": {}
}
]
},
"footer": {
"logo": {
"light": "https://896710490-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/organizations%2F2DnmWBpytIOUKeXExonU%2Fsites%2Fsite_kvXZm%2Flogo%2FdIXCjD3eQc4zZPrhKogR%2FEvolve.svg?alt=media&token=f3751721-b109-4465-8654-0fe2d78893b4",
"dark": "https://896710490-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/organizations%2F2DnmWBpytIOUKeXExonU%2Fsites%2Fsite_kvXZm%2Flogo%2FfArDZr2WyAYZFEEeE73d%2FEvolve%20Dark.svg?alt=media&token=a39fd8ed-5732-4354-ae29-1c4a0294c677"
},
"groups": [
{
"links": [
{
"to": {
"kind": "space",
"space": "w3LlITSOQye8o4wjsQXV"
},
"title": "Payments",
"localizedTitle": {
"de": "Zahlungen",
"en": "Payments",
"es": "Pagos",
"fr": "Paiements",
"zh": "支付"
}
},
{
"to": {
"kind": "space",
"space": "w7NRnYZuokE4h1mm2pJB"
},
"title": "Identity",
"localizedTitle": {
"de": "Identität",
"es": "Identidad",
"fr": "Identité",
"zh": "身份"
}
},
{
"to": {
"kind": "space",
"space": "Xtfxb7OHGyrdfIsObmnu"
},
"title": "Connect",
"localizedTitle": {
"de": "Verbinden",
"es": "Conectar",
"fr": "Connexion",
"zh": "连接"
}
}
],
"title": "Products",
"localizedTitle": {
"de": "Produkte",
"es": "Productos",
"fr": "Produits",
"zh": "产品"
}
},
{
"links": [
{
"to": {
"kind": "space",
"space": "Si95BtOt1VRLWjT7A67V"
},
"title": "Developers",
"localizedTitle": {
"de": "Entwickler",
"es": "Desarrolladores",
"fr": "Développeurs",
"zh": "开发者"
}
},
{
"to": {
"kind": "space",
"space": "Nankrp40VchJsUblU6h6"
},
"title": "Tutorials",
"localizedTitle": {
"es": "Tutoriales",
"fr": "Tutoriels",
"zh": "教程"
}
},
{
"to": {
"kind": "space",
"space": "NA4Ikc8fQtsXC5U53xJu"
},
"title": "Help Center",
"localizedTitle": {
"de": "Hilfecenter",
"es": "Centro de ayuda",
"fr": "Centre d'aide",
"zh": "帮助中心"
}
},
{
"to": {
"kind": "space",
"space": "MBT3EDUK7DzXmR0k9cje"
},
"title": "Integrations",
"localizedTitle": {
"de": "Integrationen",
"es": "Integraciones",
"fr": "Intégrations",
"zh": "集成 "
}
},
{
"to": {
"kind": "page",
"page": "UdUIt9XKFmEIOxNFFUC5",
"space": "ErQsbFsgm6eg9BApdmPl"
},
"title": "Changelog",
"localizedTitle": {
"de": "Änderungsprotokoll",
"es": "Registro de cambios",
"fr": "Journal des modifications",
"zh": "更新日志"
}
},
{
"to": {
"kind": "space",
"space": "R0VawBV5xcQ4exP2PlWS"
},
"title": "Partners",
"localizedTitle": {
"de": "Partner",
"es": "Socios",
"fr": "Partenaires",
"zh": "合作伙伴"
}
}
],
"title": "Resources",
"localizedTitle": {
"de": "Ressourcen",
"es": "Recursos",
"fr": "Ressources",
"zh": "资源"
}
},
{
"links": [
{
"to": {
"url": "https://gitbook.com/enterprise",
"kind": "url"
},
"title": "Enterprise",
"localizedTitle": {
"de": "Unternehmen",
"es": "Empresa",
"fr": "Entreprise",
"zh": "企业"
}
},
{
"to": {
"url": "https://gitbook.com/contact",
"kind": "url"
},
"title": "Contact",
"localizedTitle": {
"de": "Kontakt",
"es": "Contacto",
"zh": "联系"
}
},
{
"to": {
"url": "https://gitbook.com/docs",
"kind": "url"
},
"title": "Documentation",
"localizedTitle": {
"de": "Dokumentation",
"es": "Documentación",
"zh": "文档"
}
}
],
"title": "GitBook",
"localizedTitle": {}
}
],
"copyright": "© GitBook 2026"
},
"themes": {
"default": "system",
"toggeable": true
},
"trademark": {
"enabled": true
},
"feedback": {
"enabled": true
},
"pdf": {
"enabled": true
},
"ai": {
"mode": "assistant",
"suggestions": []
},
"advancedCustomization": {
"enabled": true
},
"pageActions": {
"externalAI": true,
"markdown": true,
"mcp": true
},
"pagination": {
"enabled": true
},
"privacyPolicy": {
"url": "https://gitbook.com/docs/policies/privacy-and-security/statement"
},
"socialPreview": {},
"socialAccounts": [
{
"handle": "gitbook",
"display": {
"footer": true,
"header": false
},
"platform": "twitter"
},
{
"handle": "GitbookIO/evolve-demo",
"display": {
"footer": true,
"header": false
},
"platform": "github"
},
{
"handle": "company/gitbook",
"display": {
"footer": true,
"header": false
},
"platform": "linkedin"
}
],
"git": {
"showEditLink": true
},
"externalLinks": {
"target": "self"
},
"insights": {
"trackingCookie": true
}
}
references/example-site/developers/openapi/v1/connect.yaml
openapi: '3.1.0'
info:
title: Evolve Connect API
version: '2026-01-15'
description: |
Onboard sellers, split payments, and pay them out — for marketplaces and platforms.
contact:
name: Evolve API support
email: support@evolve.com
servers:
- url: https://api.evolve.com/v2
description: Live
- url: https://api.test.evolve.com/v2
description: Test
security:
- bearerAuth: []
tags:
- name: connected-accounts
x-page-title: Connected accounts
x-page-icon: store
x-page-description: Manage the sellers on your platform.
- name: transfers
x-page-title: Transfers
x-page-icon: money-bill-transfer
x-page-description: Move funds between platform and seller balances.
- name: checkout-sessions
x-page-title: Checkout sessions
x-page-icon: window-maximize
x-page-description: Create hosted or embedded checkout flows for sellers.
paths:
/connected_accounts:
post:
operationId: createConnectedAccount
summary: Create a connected account
description: |
Onboard a new seller. Returns an account record and a hosted onboarding URL
you can email or surface in your own UI.
tags: [connected-accounts]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [type, country, email]
properties:
type:
type: string
enum: [individual, company]
country: { type: string, example: US }
email: { type: string, format: email }
metadata:
type: object
additionalProperties: { type: string }
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectedAccount'
get:
operationId: listConnectedAccounts
summary: List connected accounts
tags: [connected-accounts]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
responses:
'200':
description: OK
/connected_accounts/{id}:
get:
operationId: retrieveConnectedAccount
summary: Retrieve a connected account
tags: [connected-accounts]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: acct_3KsM12pL9q }
responses:
'200':
description: OK
/checkout_sessions:
post:
operationId: createCheckoutSession
summary: Create a Connect checkout session
description: |
Create a hosted or embedded checkout for a payment routed to a connected account.
Use `application_fee_amount` to set your platform's cut.
tags: [checkout-sessions]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [amount, currency, connected_account]
properties:
amount: { type: integer, example: 10000 }
currency: { type: string, example: usd }
connected_account: { type: string, example: acct_3KsM12pL9q }
application_fee_amount: { type: integer, example: 200 }
success_url: { type: string }
cancel_url: { type: string }
mode:
type: string
enum: [hosted, embedded]
default: hosted
responses:
'200':
description: OK
/transfers:
post:
operationId: createTransfer
summary: Create a transfer
description: Move funds from your platform balance to a connected account.
tags: [transfers]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [amount, currency, destination]
properties:
amount: { type: integer }
currency: { type: string }
destination: { type: string, description: Connected account id }
source_charge:
type: string
description: Optional. Tie the transfer to a specific charge.
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
ConnectedAccount:
type: object
properties:
id: { type: string, example: acct_3KsM12pL9q }
object: { type: string, enum: [connected_account] }
type: { type: string, enum: [individual, company] }
country: { type: string, example: US }
email: { type: string }
verification_status:
type: string
enum: [unverified, pending, verified, restricted]
capabilities:
type: object
properties:
charges_enabled: { type: boolean }
payouts_enabled: { type: boolean }
onboarding_url:
type: string
description: Hosted onboarding URL — valid for 24 hours.
references/example-site/developers/openapi/v1/identity.yaml
openapi: '3.1.0'
info:
title: Evolve Identity API
version: '2026-01-15'
description: |
Verify customers and partners — documents, selfies, bank accounts, and business records.
contact:
name: Evolve API support
email: support@evolve.com
servers:
- url: https://api.evolve.com/v2
description: Live
- url: https://api.test.evolve.com/v2
description: Test
security:
- bearerAuth: []
tags:
- name: verification-sessions
x-page-title: Verification sessions
x-page-icon: id-card
x-page-description: Run an identity, bank, or business verification.
- name: documents
x-page-title: Documents
x-page-icon: file-magnifying-glass
x-page-description: Inspect captured documents and their review state.
- name: bank-verifications
x-page-title: Bank verifications
x-page-icon: building-columns
x-page-description: Confirm a bank account belongs to the customer.
paths:
/verification_sessions:
post:
operationId: createVerificationSession
summary: Create a verification session
description: |
Generates a hosted verification URL and creates a session record.
Send the URL to the customer; they complete the flow and you receive a webhook.
tags: [verification-sessions]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationSessionCreate'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationSession'
get:
operationId: listVerificationSessions
summary: List verification sessions
tags: [verification-sessions]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
- name: status
in: query
schema:
type: string
enum: [pending, processing, verified, failed, manual_review]
responses:
'200':
description: OK
/verification_sessions/{id}:
get:
operationId: retrieveVerificationSession
summary: Retrieve a verification session
tags: [verification-sessions]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: vs_3KsM12pL9qXa7 }
responses:
'200':
description: OK
/bank_verifications:
post:
operationId: createBankVerification
summary: Create a bank verification
description: |
Initiate a bank-account verification. Defaults to Plaid instant when supported,
falling back to micro-deposits.
tags: [bank-verifications]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [customer]
properties:
customer: { type: string, example: cus_4n2P3qR5sT6uV }
method:
type: string
enum: [plaid, micro_deposits, auto]
default: auto
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
VerificationSession:
type: object
properties:
id: { type: string, example: vs_3KsM12pL9qXa7 }
object: { type: string, enum: [verification_session] }
type: { type: string, enum: [identity, bank, business] }
status:
type: string
enum: [pending, processing, verified, failed, manual_review]
url: { type: string, description: Hosted verification URL }
customer: { type: string }
created: { type: integer }
result:
type: object
properties:
checks:
type: array
items:
type: object
properties:
type: { type: string }
status: { type: string }
VerificationSessionCreate:
type: object
required: [type, customer]
properties:
type:
type: string
enum: [identity, bank, business]
customer: { type: string }
return_url: { type: string }
required_checks:
type: array
items: { type: string }
references/example-site/developers/openapi/v1/payments.yaml
openapi: '3.1.0'
info:
title: Evolve Payments API (v1)
version: '2025-07-01'
description: |
The legacy v1 Payments API — deprecated, sunset 2026-12-31.
v1 uses source-prefixed endpoints (`/sources/charge`, `/sources/refund`) and HMAC-SHA1
webhook signatures. New integrations should use v2. See `openapi/v2/payments.yaml`.
contact:
name: Evolve API support
email: support@evolve.com
url: https://docs.evolve.com
servers:
- url: https://api.evolve.com/v1
description: Live
- url: https://api.test.evolve.com/v1
description: Test
security:
- bearerAuth: []
tags:
- name: charges
x-page-title: Charges
x-page-icon: credit-card
x-page-description: Source-prefixed charge endpoints (legacy).
- name: refunds
x-page-title: Refunds
x-page-icon: rotate-left
x-page-description: Source-prefixed refund endpoints (legacy).
- name: payouts
x-page-title: Payouts
x-page-icon: money-bill-transfer
x-page-description: Move funds from your Evolve balance to your bank.
paths:
/sources/charge:
post:
operationId: createSourceCharge
summary: Charge a source
deprecated: true
description: |
Charge a card or bank-account source. **Deprecated** in favor of v2's `POST /charges`.
v1 source-prefixed endpoints will return 410 Gone after 2026-12-31.
tags: [charges]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SourceChargeCreate'
responses:
'200':
description: Charge succeeded
content:
application/json:
schema:
$ref: '#/components/schemas/Charge'
'402':
description: Card declined
/sources/refund:
post:
operationId: createSourceRefund
summary: Refund a charge
deprecated: true
description: |
Refund a previous charge. **Deprecated** in favor of v2's `POST /refunds`.
tags: [refunds]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [charge]
properties:
charge: { type: string, example: ch_3KsM12pL9qXa7 }
amount: { type: integer }
responses:
'200':
description: OK
/charges/{id}:
get:
operationId: retrieveCharge
summary: Retrieve a charge
tags: [charges]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: ch_3KsM12pL9qXa7 }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Charge' }
/payouts:
get:
operationId: listPayouts
summary: List payouts
tags: [payouts]
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: sk_live_… or sk_test_…
schemas:
Charge:
type: object
properties:
id: { type: string, example: ch_3KsM12pL9qXa7 }
object: { type: string, enum: [charge] }
amount: { type: integer }
currency: { type: string, example: usd }
status:
type: string
enum: [pending, succeeded, failed, refunded]
description: |
Note: v1 has a flatter status enum. v2 introduced `authorized`, `captured`,
`voided`, `disputed` as distinct states.
captured: { type: boolean }
created: { type: integer }
SourceChargeCreate:
type: object
required: [amount, currency, source]
properties:
amount: { type: integer, example: 4200 }
currency: { type: string, example: usd }
source:
type: string
description: |
Card token or bank-account token. v1 used different endpoints per source type;
v2 unified them under a single `/charges` endpoint that infers source type from
the token prefix.
example: tok_visa
description: { type: string }
references/example-site/developers/openapi/v2/connect.yaml
openapi: '3.1.0'
info:
title: Evolve Connect API
version: '2026-01-15'
description: |
Onboard sellers, split payments, and pay them out — for marketplaces and platforms.
contact:
name: Evolve API support
email: support@evolve.com
servers:
- url: https://api.evolve.com/v2
description: Live
- url: https://api.test.evolve.com/v2
description: Test
security:
- bearerAuth: []
tags:
- name: connected-accounts
x-page-title: Connected accounts
x-page-icon: store
x-page-description: Manage the sellers on your platform.
- name: transfers
x-page-title: Transfers
x-page-icon: money-bill-transfer
x-page-description: Move funds between platform and seller balances.
- name: checkout-sessions
x-page-title: Checkout sessions
x-page-icon: window-maximize
x-page-description: Create hosted or embedded checkout flows for sellers.
paths:
/connected_accounts:
post:
operationId: createConnectedAccount
summary: Create a connected account
description: |
Onboard a new seller. Returns an account record and a hosted onboarding URL
you can email or surface in your own UI.
tags: [connected-accounts]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [type, country, email]
properties:
type:
type: string
enum: [individual, company]
country: { type: string, example: US }
email: { type: string, format: email }
metadata:
type: object
additionalProperties: { type: string }
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectedAccount'
get:
operationId: listConnectedAccounts
summary: List connected accounts
tags: [connected-accounts]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
responses:
'200':
description: OK
/connected_accounts/{id}:
get:
operationId: retrieveConnectedAccount
summary: Retrieve a connected account
tags: [connected-accounts]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: acct_3KsM12pL9q }
responses:
'200':
description: OK
/checkout_sessions:
post:
operationId: createCheckoutSession
summary: Create a Connect checkout session
description: |
Create a hosted or embedded checkout for a payment routed to a connected account.
Use `application_fee_amount` to set your platform's cut.
tags: [checkout-sessions]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [amount, currency, connected_account]
properties:
amount: { type: integer, example: 10000 }
currency: { type: string, example: usd }
connected_account: { type: string, example: acct_3KsM12pL9q }
application_fee_amount: { type: integer, example: 200 }
success_url: { type: string }
cancel_url: { type: string }
mode:
type: string
enum: [hosted, embedded]
default: hosted
responses:
'200':
description: OK
/transfers:
post:
operationId: createTransfer
summary: Create a transfer
description: Move funds from your platform balance to a connected account.
tags: [transfers]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [amount, currency, destination]
properties:
amount: { type: integer }
currency: { type: string }
destination: { type: string, description: Connected account id }
source_charge:
type: string
description: Optional. Tie the transfer to a specific charge.
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
ConnectedAccount:
type: object
properties:
id: { type: string, example: acct_3KsM12pL9q }
object: { type: string, enum: [connected_account] }
type: { type: string, enum: [individual, company] }
country: { type: string, example: US }
email: { type: string }
verification_status:
type: string
enum: [unverified, pending, verified, restricted]
capabilities:
type: object
properties:
charges_enabled: { type: boolean }
payouts_enabled: { type: boolean }
onboarding_url:
type: string
description: Hosted onboarding URL — valid for 24 hours.
references/example-site/developers/openapi/v2/identity.yaml
openapi: '3.1.0'
info:
title: Evolve Identity API
version: '2026-01-15'
description: |
Verify customers and partners — documents, selfies, bank accounts, and business records.
contact:
name: Evolve API support
email: support@evolve.com
servers:
- url: https://api.evolve.com/v2
description: Live
- url: https://api.test.evolve.com/v2
description: Test
security:
- bearerAuth: []
tags:
- name: verification-sessions
x-page-title: Verification sessions
x-page-icon: id-card
x-page-description: Run an identity, bank, or business verification.
- name: documents
x-page-title: Documents
x-page-icon: file-magnifying-glass
x-page-description: Inspect captured documents and their review state.
- name: bank-verifications
x-page-title: Bank verifications
x-page-icon: building-columns
x-page-description: Confirm a bank account belongs to the customer.
paths:
/verification_sessions:
post:
operationId: createVerificationSession
summary: Create a verification session
description: |
Generates a hosted verification URL and creates a session record.
Send the URL to the customer; they complete the flow and you receive a webhook.
tags: [verification-sessions]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationSessionCreate'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationSession'
get:
operationId: listVerificationSessions
summary: List verification sessions
tags: [verification-sessions]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
- name: status
in: query
schema:
type: string
enum: [pending, processing, verified, failed, manual_review]
responses:
'200':
description: OK
/verification_sessions/{id}:
get:
operationId: retrieveVerificationSession
summary: Retrieve a verification session
tags: [verification-sessions]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: vs_3KsM12pL9qXa7 }
responses:
'200':
description: OK
/bank_verifications:
post:
operationId: createBankVerification
summary: Create a bank verification
description: |
Initiate a bank-account verification. Defaults to Plaid instant when supported,
falling back to micro-deposits.
tags: [bank-verifications]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [customer]
properties:
customer: { type: string, example: cus_4n2P3qR5sT6uV }
method:
type: string
enum: [plaid, micro_deposits, auto]
default: auto
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
VerificationSession:
type: object
properties:
id: { type: string, example: vs_3KsM12pL9qXa7 }
object: { type: string, enum: [verification_session] }
type: { type: string, enum: [identity, bank, business] }
status:
type: string
enum: [pending, processing, verified, failed, manual_review]
url: { type: string, description: Hosted verification URL }
customer: { type: string }
created: { type: integer }
result:
type: object
properties:
checks:
type: array
items:
type: object
properties:
type: { type: string }
status: { type: string }
VerificationSessionCreate:
type: object
required: [type, customer]
properties:
type:
type: string
enum: [identity, bank, business]
customer: { type: string }
return_url: { type: string }
required_checks:
type: array
items: { type: string }
references/example-site/developers/openapi/v2/payments.yaml
openapi: '3.1.0'
info:
title: Evolve Payments API (v2)
version: '2026-01-15'
description: |
Accept card and bank-rail payments, issue refunds, and reconcile settlements.
This is the **v2** spec — the stable, default variant. For older deployments see
`openapi/v1/payments.yaml`; for the preview shape see `openapi/v3/payments.yaml`.
contact:
name: Evolve API support
email: support@evolve.com
url: https://docs.evolve.com
servers:
- url: https://api.evolve.com/v2
description: Live
- url: https://api.test.evolve.com/v2
description: Test
security:
- bearerAuth: []
tags:
- name: charges
x-page-title: Charges
x-page-icon: credit-card
x-page-description: Create, capture, and manage payments.
- name: refunds
x-page-title: Refunds
x-page-icon: rotate-left
x-page-description: Return funds to a customer.
- name: payouts
x-page-title: Payouts
x-page-icon: money-bill-transfer
x-page-description: Move funds from your Evolve balance to your bank.
- name: balance
x-page-title: Balance
x-page-icon: scale-balanced
x-page-description: Check your current available and pending funds.
paths:
/charges:
post:
operationId: createCharge
summary: Create a charge
description: |
Charge a payment method. The default behavior is **authorize-and-capture in one step**.
Pass `capture: false` for two-step.
tags: [charges]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChargeCreate'
responses:
'200':
description: Charge succeeded
content:
application/json:
schema:
$ref: '#/components/schemas/Charge'
'402':
description: Card declined
'422':
description: Processing error — retry with same idempotency key
get:
operationId: listCharges
summary: List charges
description: Returns a paginated list of charges, most recent first.
tags: [charges]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/Charge' }
has_more: { type: boolean }
next_cursor: { type: string, nullable: true }
/charges/{id}:
get:
operationId: retrieveCharge
summary: Retrieve a charge
tags: [charges]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: ch_3KsM12pL9qXa7 }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Charge' }
/charges/{id}/capture:
post:
operationId: captureCharge
summary: Capture an authorized charge
description: Capture an `authorized` charge. Must be within 7 days of authorization.
tags: [charges]
parameters:
- name: id
in: path
required: true
schema: { type: string }
requestBody:
content:
application/json:
schema:
type: object
properties:
amount:
type: integer
description: Optional. Defaults to the original authorized amount.
responses:
'200':
description: OK
/refunds:
post:
operationId: createRefund
summary: Create a refund
tags: [refunds]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [charge]
properties:
charge: { type: string, example: ch_3KsM12pL9qXa7 }
amount: { type: integer, description: Optional partial refund amount }
reason:
type: string
enum: [duplicate, fraudulent, requested_by_customer]
responses:
'200':
description: OK
/payouts:
get:
operationId: listPayouts
summary: List payouts
tags: [payouts]
responses:
'200':
description: OK
/balance:
get:
operationId: retrieveBalance
summary: Retrieve balance
description: Returns your current available, pending, and reserved balances.
tags: [balance]
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
available:
type: array
items: { $ref: '#/components/schemas/Money' }
pending:
type: array
items: { $ref: '#/components/schemas/Money' }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: sk_live_… or sk_test_…
schemas:
Charge:
type: object
properties:
id: { type: string, example: ch_3KsM12pL9qXa7 }
object: { type: string, enum: [charge] }
amount: { type: integer, description: Amount in the smallest currency unit (cents) }
currency: { type: string, example: usd }
status:
type: string
enum: [pending, authorized, captured, voided, refunded, disputed, failed]
captured: { type: boolean }
created: { type: integer, description: Unix timestamp }
payment_method:
type: object
properties:
type: { type: string, enum: [card, ach_debit, wire, sepa] }
brand: { type: string, example: visa }
last4: { type: string, example: '4242' }
ChargeCreate:
type: object
required: [amount, currency, source]
properties:
amount: { type: integer, example: 4200 }
currency: { type: string, example: usd }
source:
type: string
description: Token, saved payment method id, or PaymentSession id.
example: tok_visa
capture:
type: boolean
default: true
description: When false, creates an authorization that must be captured within 7 days.
description: { type: string }
metadata:
type: object
additionalProperties: { type: string }
Money:
type: object
properties:
amount: { type: integer }
currency: { type: string }
references/example-site/developers/openapi/v3/connect.yaml
openapi: '3.1.0'
info:
title: Evolve Connect API
version: '2026-01-15'
description: |
Onboard sellers, split payments, and pay them out — for marketplaces and platforms.
contact:
name: Evolve API support
email: support@evolve.com
servers:
- url: https://api.evolve.com/v2
description: Live
- url: https://api.test.evolve.com/v2
description: Test
security:
- bearerAuth: []
tags:
- name: connected-accounts
x-page-title: Connected accounts
x-page-icon: store
x-page-description: Manage the sellers on your platform.
- name: transfers
x-page-title: Transfers
x-page-icon: money-bill-transfer
x-page-description: Move funds between platform and seller balances.
- name: checkout-sessions
x-page-title: Checkout sessions
x-page-icon: window-maximize
x-page-description: Create hosted or embedded checkout flows for sellers.
paths:
/connected_accounts:
post:
operationId: createConnectedAccount
summary: Create a connected account
description: |
Onboard a new seller. Returns an account record and a hosted onboarding URL
you can email or surface in your own UI.
tags: [connected-accounts]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [type, country, email]
properties:
type:
type: string
enum: [individual, company]
country: { type: string, example: US }
email: { type: string, format: email }
metadata:
type: object
additionalProperties: { type: string }
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectedAccount'
get:
operationId: listConnectedAccounts
summary: List connected accounts
tags: [connected-accounts]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
responses:
'200':
description: OK
/connected_accounts/{id}:
get:
operationId: retrieveConnectedAccount
summary: Retrieve a connected account
tags: [connected-accounts]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: acct_3KsM12pL9q }
responses:
'200':
description: OK
/checkout_sessions:
post:
operationId: createCheckoutSession
summary: Create a Connect checkout session
description: |
Create a hosted or embedded checkout for a payment routed to a connected account.
Use `application_fee_amount` to set your platform's cut.
tags: [checkout-sessions]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [amount, currency, connected_account]
properties:
amount: { type: integer, example: 10000 }
currency: { type: string, example: usd }
connected_account: { type: string, example: acct_3KsM12pL9q }
application_fee_amount: { type: integer, example: 200 }
success_url: { type: string }
cancel_url: { type: string }
mode:
type: string
enum: [hosted, embedded]
default: hosted
responses:
'200':
description: OK
/transfers:
post:
operationId: createTransfer
summary: Create a transfer
description: Move funds from your platform balance to a connected account.
tags: [transfers]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [amount, currency, destination]
properties:
amount: { type: integer }
currency: { type: string }
destination: { type: string, description: Connected account id }
source_charge:
type: string
description: Optional. Tie the transfer to a specific charge.
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
ConnectedAccount:
type: object
properties:
id: { type: string, example: acct_3KsM12pL9q }
object: { type: string, enum: [connected_account] }
type: { type: string, enum: [individual, company] }
country: { type: string, example: US }
email: { type: string }
verification_status:
type: string
enum: [unverified, pending, verified, restricted]
capabilities:
type: object
properties:
charges_enabled: { type: boolean }
payouts_enabled: { type: boolean }
onboarding_url:
type: string
description: Hosted onboarding URL — valid for 24 hours.
references/example-site/developers/openapi/v3/identity.yaml
openapi: '3.1.0'
info:
title: Evolve Identity API
version: '2026-01-15'
description: |
Verify customers and partners — documents, selfies, bank accounts, and business records.
contact:
name: Evolve API support
email: support@evolve.com
servers:
- url: https://api.evolve.com/v2
description: Live
- url: https://api.test.evolve.com/v2
description: Test
security:
- bearerAuth: []
tags:
- name: verification-sessions
x-page-title: Verification sessions
x-page-icon: id-card
x-page-description: Run an identity, bank, or business verification.
- name: documents
x-page-title: Documents
x-page-icon: file-magnifying-glass
x-page-description: Inspect captured documents and their review state.
- name: bank-verifications
x-page-title: Bank verifications
x-page-icon: building-columns
x-page-description: Confirm a bank account belongs to the customer.
paths:
/verification_sessions:
post:
operationId: createVerificationSession
summary: Create a verification session
description: |
Generates a hosted verification URL and creates a session record.
Send the URL to the customer; they complete the flow and you receive a webhook.
tags: [verification-sessions]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationSessionCreate'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/VerificationSession'
get:
operationId: listVerificationSessions
summary: List verification sessions
tags: [verification-sessions]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
- name: status
in: query
schema:
type: string
enum: [pending, processing, verified, failed, manual_review]
responses:
'200':
description: OK
/verification_sessions/{id}:
get:
operationId: retrieveVerificationSession
summary: Retrieve a verification session
tags: [verification-sessions]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: vs_3KsM12pL9qXa7 }
responses:
'200':
description: OK
/bank_verifications:
post:
operationId: createBankVerification
summary: Create a bank verification
description: |
Initiate a bank-account verification. Defaults to Plaid instant when supported,
falling back to micro-deposits.
tags: [bank-verifications]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [customer]
properties:
customer: { type: string, example: cus_4n2P3qR5sT6uV }
method:
type: string
enum: [plaid, micro_deposits, auto]
default: auto
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
schemas:
VerificationSession:
type: object
properties:
id: { type: string, example: vs_3KsM12pL9qXa7 }
object: { type: string, enum: [verification_session] }
type: { type: string, enum: [identity, bank, business] }
status:
type: string
enum: [pending, processing, verified, failed, manual_review]
url: { type: string, description: Hosted verification URL }
customer: { type: string }
created: { type: integer }
result:
type: object
properties:
checks:
type: array
items:
type: object
properties:
type: { type: string }
status: { type: string }
VerificationSessionCreate:
type: object
required: [type, customer]
properties:
type:
type: string
enum: [identity, bank, business]
customer: { type: string }
return_url: { type: string }
required_checks:
type: array
items: { type: string }
references/example-site/developers/openapi/v3/payments.yaml
openapi: '3.1.0'
info:
title: Evolve Payments API (v3 preview)
version: '2026-04-01-preview'
description: |
The v3 preview Payments API.
v3 renames `Charge` to `Payment`, replaces `capture: true|false` with a `capture_method`
enum, extends authorization windows to 30 days, and supports multi-currency capture.
**This is a preview API.** Endpoints and shapes may change. Don't run production traffic
on v3 without coordinating with your account team.
contact:
name: Evolve API support
email: support@evolve.com
url: https://docs.evolve.com
servers:
- url: https://api.evolve.com/v3
description: Live (preview)
- url: https://api.test.evolve.com/v3
description: Test (preview)
security:
- bearerAuth: []
tags:
- name: payments
x-page-title: Payments
x-page-icon: credit-card
x-page-description: Create, capture, void, retrieve. Renamed from "Charges" in v3.
- name: refunds
x-page-title: Refunds
x-page-icon: rotate-left
x-page-description: Refund a payment.
- name: payouts
x-page-title: Payouts
x-page-icon: money-bill-transfer
x-page-description: Move funds from your Evolve balance to your bank.
- name: balance
x-page-title: Balance
x-page-icon: scale-balanced
x-page-description: Check your current available, pending, and reserved funds.
paths:
/payments:
post:
operationId: createPayment
summary: Create a payment
description: |
Charge a payment method. The default behavior is **automatic capture in one step**.
Set `capture_method: manual` for two-step authorize-then-capture, or `automatic_async`
for batched capture (common in marketplaces).
tags: [payments]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentCreate'
responses:
'200':
description: Payment succeeded
content:
application/json:
schema:
$ref: '#/components/schemas/Payment'
'402':
description: Card declined
'422':
description: Processing error — retry with same idempotency key
get:
operationId: listPayments
summary: List payments
tags: [payments]
parameters:
- name: limit
in: query
schema: { type: integer, default: 100, maximum: 1000 }
- name: cursor
in: query
schema: { type: string }
responses:
'200':
description: OK
/payments/{id}:
get:
operationId: retrievePayment
summary: Retrieve a payment
tags: [payments]
parameters:
- name: id
in: path
required: true
schema: { type: string, example: pay_3KsM12pL9qXa7 }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Payment' }
/payments/{id}/capture:
post:
operationId: capturePayment
summary: Capture an authorized payment
description: |
Capture an `authorized` payment. **v3 extends the authorization window to 30 days**
from v2's 7. Multi-currency capture is supported — pass `capture_currency` and
`capture_amount` to capture in a different currency than the original authorization.
tags: [payments]
parameters:
- name: id
in: path
required: true
schema: { type: string }
requestBody:
content:
application/json:
schema:
type: object
properties:
capture_amount: { type: integer }
capture_currency:
type: string
description: Currency to capture in. Defaults to the authorization currency.
responses:
'200':
description: OK
/refunds:
post:
operationId: createRefund
summary: Create a refund
tags: [refunds]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [payment]
properties:
payment:
type: string
example: pay_3KsM12pL9qXa7
description: |
Payment ID. Renamed from `charge` in v3 — use `payment` here, not `charge`.
amount: { type: integer }
reason:
type: string
enum: [duplicate, fraudulent, requested_by_customer]
responses:
'200':
description: OK
/balance:
get:
operationId: retrieveBalance
summary: Retrieve balance
tags: [balance]
responses:
'200':
description: OK
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: sk_live_… or sk_test_…
schemas:
Payment:
type: object
description: Renamed from `Charge` in v3. Same lifecycle, same identifiers (now `pay_*`).
properties:
id: { type: string, example: pay_3KsM12pL9qXa7 }
object: { type: string, enum: [payment] }
amount: { type: integer }
currency: { type: string, example: usd }
status:
type: string
enum: [pending, authorized, captured, voided, refunded, disputed, failed]
capture_method:
type: string
enum: [automatic, manual, automatic_async]
authorize_amount: { type: integer, nullable: true }
authorize_currency: { type: string, nullable: true }
capture_amount: { type: integer, nullable: true }
capture_currency: { type: string, nullable: true }
created: { type: integer }
payment_method:
type: object
properties:
type: { type: string, enum: [card, ach_debit, wire, sepa] }
brand: { type: string, example: visa }
last4: { type: string, example: '4242' }
PaymentCreate:
type: object
required: [amount, currency, source]
properties:
amount: { type: integer, example: 4200 }
currency: { type: string, example: usd }
source: { type: string, example: tok_visa }
capture_method:
type: string
enum: [automatic, manual, automatic_async]
default: automatic
description: |
Replaces v2's `capture: true|false` boolean. `automatic` captures immediately,
`manual` creates an authorization to capture later (within 30 days), and
`automatic_async` captures in a batch within 24 hours.
capture_currency:
type: string
description: |
For multi-currency capture. If different from `currency`, Evolve converts at
the daily wholesale rate plus your configured FX margin at capture time.
description: { type: string }
metadata:
type: object
additionalProperties: { type: string }
references/example-site/developers/v2/.gitbook/vars.yaml
api_live: https://api.evolve.com
api_test: https://api.test.evolve.com
dashboard_live: https://dashboard.evolve.com
dashboard_test: https://dashboard.test.evolve.com
docs_root: https://docs.evolve.com
support_email: support@evolve.com
status_page: https://status.evolve.com
variant: v2
variant_status: stable
default_rate_limit: 500
node_pkg: '@evolve/node'
python_pkg: evolve
go_pkg: github.com/evolve-pay/evolve-go
ruby_pkg: evolve
references/example-site/developers/v2/connect-api/README.md
---
icon: circles-overlap
description: Connected accounts, transfers, and checkout sessions for marketplace platforms.
---
# Connect API
The Connect API extends Payments with the platform-specific resources — connected accounts (sellers), transfers (moving funds to sellers), and checkout sessions (taking payments on a seller's behalf). The operation reference is auto-generated and listed below this page in the sidebar.
## Resources
* **Connected accounts** — the sellers on your platform. One per seller.
* **Transfers** — move funds from your platform balance to a connected account.
* **Checkout sessions** — hosted or embedded checkout for a Connect payment.
## A minimal flow
```mermaid
flowchart LR
A[POST /connected_accounts] --> B[Seller onboards via hosted URL]
B --> C[POST /checkout_sessions]
C --> D[Buyer pays]
D --> E[Funds split: platform fee + seller balance]
E --> F[Seller payout on schedule]
```
## Application fees
Every checkout session can carry an `application_fee_amount` — your platform's cut, in the smallest currency unit:
```http
POST /v2/checkout_sessions
{
"amount": 10000,
"currency": "usd",
"connected_account": "acct_3KsM12pL9q",
"application_fee_amount": 200
}
```
That charges the buyer $100, transfers $98 to the seller's connected account, and credits $2 to your platform balance. By default the card processing fee is netted from your application fee — see [Connect → Splitting payments](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/platform-setup/splitting-payments) for the full mechanics including pass-through fees.
## Direct charges vs destination charges
Two patterns for routing a payment to a seller:
| Pattern | When to use |
| --- | --- |
| **Direct charge** | The seller is the merchant of record. Set `Evolve-Account: acct_*` header on the charge. The seller's statement descriptor appears on the buyer's card statement. |
| **Destination charge** | The platform is the merchant of record. Charge happens on the platform; a transfer moves the funds to the connected account. The platform's descriptor appears on the buyer's statement. Most Connect platforms use this. |
Choose at the platform level by setting your default in **Connect → Settings → Charge type** in the dashboard. Override per checkout session if needed.
## Conceptual background
For the product-side concepts — onboarding flow, payout scheduling, dispute routing, refund splits — see the [Connect product space](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/).
references/example-site/developers/v2/getting-started/authentication.md
---
description: API keys, scopes, and how Evolve authenticates every request.
icon: key
---
# Authentication
Evolve uses bearer-token authentication. Every request includes an `Authorization` header with a secret key:
```http
Authorization: Bearer sk_live_4gT8m...
```
Keys are issued and rotated from the \[dashboard]\(<code class="expression">space.vars.dashboard_live</code>) under **Developers → API keys**. We recommend rotating production keys every 90 days.
## Key types
| Key | Prefix | Use it for | Safe to expose? |
| ---------------- | ----------------------- | ------------------------------------------------- | --------------- |
| Live secret | `sk_live_` | Server-side production calls | No |
| Live publishable | `pk_live_` | Client-side payment session creation | Yes |
| Test secret | `sk_test_` | Server-side test calls | No |
| Test publishable | `pk_test_` | Client-side test integrations | Yes |
| Restricted | `rk_live_` / `rk_test_` | Scoped server keys (read-only, refund-only, etc.) | No |
## Setting the key in each SDK
{% tabs %}
{% tab title="Node" %}
```js
import Evolve from "@evolve/node";
// Reads from EVOLVE_SECRET_KEY by default.
const evolve = new Evolve();
// Or pass explicitly:
const evolve = new Evolve(process.env.EVOLVE_SECRET_KEY);
```
{% endtab %}
{% tab title="Python" %}
```python
import os
import evolve
evolve.api_key = os.environ["EVOLVE_SECRET_KEY"]
```
{% endtab %}
{% tab title="Go" %}
```go
import (
"os"
"github.com/evolve-pay/evolve-go"
)
evolve.Key = os.Getenv("EVOLVE_SECRET_KEY")
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
require "evolve"
Evolve.api_key = ENV["EVOLVE_SECRET_KEY"]
```
{% endtab %}
{% tab title="cURL" %}
```bash
curl https://api.evolve.com/v2/charges \
-H "Authorization: Bearer $EVOLVE_SECRET_KEY"
```
{% endtab %}
{% endtabs %}
## Restricted keys
If you need to grant a third party (BI tool, internal microservice, on-call dashboard) access to a subset of your account, create a **restricted key** under **Developers → API keys → New restricted key**. You pick the resources and permissions; the key can never be widened after creation.
Common restricted-key shapes:
| Shape | Use case |
| ----------------- | ------------------------------------------ |
| Read-only | BI exports, monitoring dashboards |
| Refunds only | Customer-support tooling |
| Connect read-only | Per-platform reporting on Connect activity |
| Webhooks only | Dedicated event-handler service |
## API versioning
Evolve uses **dated versioning** via the `Evolve-Version` header. The default version for your account is set when you create your first API key, and you can override it per request:
```http
Evolve-Version: 2026-01-15
```
Major shape changes (resources renamed, fields removed) ship as **variants** rather than new dates — see [Payments API → Overview](../payments-api/) for `v1`, `v2`, and `v3`.
If you don't send the header, your account-default version is used. We recommend pinning explicitly in production code.
## Verifying webhook signatures
Webhooks and partner callbacks are signed with HMAC-SHA256 using a separate **signing secret** (prefix `whsec_`). Always verify the signature before acting on the payload — a missing or invalid signature means the request did not come from Evolve.
See [Verifying signatures](../webhooks/verifying-signatures.md) for the per-language code.
{% hint style="danger" %}
**Never disable signature verification "temporarily" in production.** This is the single most common source of payment-platform incidents we see. If you can't verify, fail closed.
{% endhint %}
## Rotating keys
You can rotate any secret key without downtime:
{% stepper %}
{% step %}
#### Generate a new key
In the dashboard, click **Roll** next to the key. The new key becomes active immediately. The old key keeps working for 24 hours so you can deploy at your own pace.
{% endstep %}
{% step %}
#### Deploy the new key
Update your environment configuration and redeploy. Verify by making a test call against the live API.
{% endstep %}
{% step %}
#### Revoke the old key
Once you've confirmed the new key is in place everywhere, click **Revoke**. Any further requests with the old key will fail with `401 unauthorized`.
{% endstep %}
{% endstepper %}
## Detection and incident response
If a key is exposed (committed to a public repo, leaked in a log), revoke it immediately from the dashboard. Evolve also runs leaked-key detection across public GitHub and a few other surfaces — if we find your key in a public commit, we'll auto-revoke it and email you within minutes.
## Related
* [Quickstart](quickstart.md) — minimal setup walkthrough.
* [Conventions](conventions.md) — what each request and response look like.
* [Webhooks → Verifying signatures](../webhooks/verifying-signatures.md) — for inbound auth.
references/example-site/developers/v2/getting-started/conventions.md
---
icon: list-ul
description: Errors, idempotency, pagination, rate limits — the things that look the same on every endpoint.
---
# Conventions
The Evolve API is plain HTTPS plus JSON. Every endpoint follows the same conventions for errors, retries, pagination, and rate limiting — get them right once and they apply everywhere.
## Request shape
* **Base URL:** <code class="expression">space.vars.api_live</code> (live), <code class="expression">space.vars.api_test</code> (test).
* **Auth:** `Authorization: Bearer sk_*` — see [Authentication](authentication.md).
* **Content type:** JSON for write operations. Form-encoded also accepted for convenience with `curl`.
* **Versioning:** `Evolve-Version: 2026-01-15` header. Defaults to your account version.
## Errors
Every error response has the same shape. Build your error handling around the `code`, never the human-readable message text.
```json
{
"error": {
"type": "card_error",
"code": "card_declined",
"decline_code": "insufficient_funds",
"message": "Your card has insufficient funds.",
"param": "source",
"request_id": "req_8h2nF6m4Lp"
}
}
```
### Common codes
| HTTP | `code` | When you'll see it | Retry safe? |
| --- | --- | --- | --- |
| `400` | `invalid_request` | Malformed body, missing required field, unknown parameter | No |
| `401` | `unauthorized` | Missing or invalid API key | No |
| `402` | `card_declined` | Issuer declined the charge | Sometimes — see `decline_code` |
| `409` | `idempotency_conflict` | Same `Idempotency-Key` reused with a different request body | No |
| `422` | `processing_error` | Network-level processing failure | Yes — retry with same key |
| `429` | `rate_limited` | You exceeded your account's request rate | Yes — back off |
| `500` | `api_error` | Evolve-side error | Yes — retry with same key |
Always log `request_id` from the response — it's what we'll need to investigate.
## Idempotency
Every write endpoint accepts an `Idempotency-Key` header. Send a unique key (we recommend UUID v4 or v7) per logical operation. If a request with the same key arrives within **24 hours**, Evolve returns the original response instead of creating a duplicate.
```http
POST /v2/charges
Authorization: Bearer sk_live_...
Idempotency-Key: 7b8f2c4a-91d2-4fe1-9b6a-2c8e5f1a9b0c
Content-Type: application/json
{ "amount": 4200, "currency": "usd", "source": "tok_visa" }
```
Rules:
1. Generate the key on the **server**, not the client. A double-clicked button shouldn't produce two distinct keys.
2. Use a fresh key per logical operation. Never reuse keys across operations.
3. Persist the key alongside the operation in your database **before** sending the request — that way a retry after a process crash uses the same key.
If you send the same key with a *different* body, you get `409 idempotency_conflict`. This is intentional — it catches bugs.
{% hint style="info" %}
The official [SDKs](sdks.md) auto-generate an idempotency key for every write call unless you provide one. You can always pass your own.
{% endhint %}
## Retry strategy
For retryable errors (5xx, 422, 429), use exponential backoff with full jitter, capped at 60 seconds. The official SDKs do this automatically; here's the pattern if you're rolling your own:
{% tabs %}
{% tab title="Node" %}
```js
async function withRetry(fn, max = 6) {
for (let i = 0; i < max; i++) {
try { return await fn(); }
catch (err) {
if (!isRetryable(err) || i === max - 1) throw err;
const delay = Math.min(60_000, (2 ** i) * 1000 + Math.random() * 1000);
await new Promise(r => setTimeout(r, delay));
}
}
}
```
{% endtab %}
{% tab title="Python" %}
```python
import random, time
def with_retry(fn, max_attempts=6):
for attempt in range(max_attempts):
try:
return fn()
except RetryableError:
if attempt == max_attempts - 1:
raise
sleep = min(60, (2 ** attempt) + random.random())
time.sleep(sleep)
```
{% endtab %}
{% tab title="Go" %}
```go
func withRetry(fn func() error, max int) error {
for i := 0; i < max; i++ {
if err := fn(); err == nil || !isRetryable(err) {
return err
}
if i == max-1 {
return fmt.Errorf("retries exhausted")
}
delay := time.Duration(math.Min(60, math.Pow(2, float64(i))+rand.Float64())) * time.Second
time.Sleep(delay)
}
return nil
}
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
def with_retry(max = 6)
attempts = 0
begin
yield
rescue RetryableError
attempts += 1
raise if attempts >= max
sleep [60, (2 ** attempts) + rand].min
retry
end
end
```
{% endtab %}
{% endtabs %}
## Pagination
List endpoints return cursor-paginated responses:
```json
{
"data": [ { "id": "ch_..." }, { "id": "ch_..." }, ... ],
"has_more": true,
"next_cursor": "cur_8M2nF6m4LpQ"
}
```
* Default page size: **100**. Maximum: **1000**. Set with `?limit=`.
* Pass `next_cursor` from the previous response as `?cursor=` to fetch the next page.
* When `has_more` is `false`, you've reached the end.
The SDKs expose iterators that handle pagination automatically:
{% tabs %}
{% tab title="Node" %}
```js
for await (const charge of evolve.charges.list({ limit: 100 })) {
console.log(charge.id);
}
```
{% endtab %}
{% tab title="Python" %}
```python
for charge in evolve.Charge.list(limit=100).auto_paging_iter():
print(charge.id)
```
{% endtab %}
{% tab title="Go" %}
```go
iter := charge.List(&evolve.ChargeListParams{Limit: evolve.Int64(100)})
for iter.Next() {
fmt.Println(iter.Charge().ID)
}
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
Evolve::Charge.list(limit: 100).auto_paging_each do |charge|
puts charge.id
end
```
{% endtab %}
{% endtabs %}
## Rate limits
| Plan | Default rate |
| --- | --- |
| Starter | 100 req/s |
| Growth | <code class="expression">space.vars.default_rate_limit</code> req/s |
| Enterprise | Custom — typically 2,000+ req/s |
Every response includes:
```http
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 487
X-RateLimit-Reset: 1714477260
```
If you hit the limit, you get `429 rate_limited` and a `Retry-After: <seconds>` header. The SDKs handle backoff automatically.
For sustained loads above your tier's default, contact your account team — we can raise it.
## Object IDs
Every Evolve object has a stable, prefixed ID:
| Prefix | Resource |
| --- | --- |
| `ch_` | Charge |
| `re_` | Refund |
| `dp_` | Dispute |
| `po_` | Payout |
| `cus_` | Customer |
| `vs_` | Verification session (Identity) |
| `acct_` | Connected account (Connect) |
| `tr_` | Transfer (Connect) |
| `cs_` | Checkout session |
| `req_` | Request (in error responses) |
Test-mode and live-mode IDs share the same prefix structure, but objects from one environment are never accessible from the other.
## Timestamps and money
* **Timestamps**: Unix seconds (UTC).
* **Amounts**: integer in the smallest currency unit (cents for USD, EUR; yen for JPY). Always pair with `currency`.
## What's next
* [SDKs](sdks.md) — pick a language and let the conventions handle themselves.
* [Webhooks](../webhooks/README.md) — same conventions apply on inbound events.
* [API references](../payments-api/README.md) — endpoint-by-endpoint detail.
references/example-site/developers/v2/getting-started/for-ai-agents.md
---
icon: robot
description: How AI agents and code copilots can navigate the Evolve docs and call the API directly.
---
# For AI agents
Evolve publishes structured indexes and a hosted MCP server so AI assistants can find what they need without spidering HTML. If you're building an agent that integrates with Evolve, or you're using a code copilot to write Evolve integrations, this page is the entry point.
## llms.txt and llms-full.txt
We follow the [llms.txt convention](https://llmstxt.org). Two files at the docs root:
| File | Contents | Best for |
| --- | --- | --- |
| [https://docs.evolve.com/llms.txt](https://gitbook.com) | Title, description, and a curated list of links to every section of the docs. | Agents that need a map of the docs to navigate by. |
| [https://docs.evolve.com/llms-full.txt](https://gitbook.com) | The complete content of every public page concatenated into one file. | Agents that want the full docs in their context window. |
Both are auto-generated from the same content as the rendered docs site, and refreshed on every publish. Cache for at most an hour.
### Using llms.txt
```
# Evolve API documentation
> The Evolve API lets you accept payments, verify identities, and run a marketplace platform.
## API references
- [Payments API](https://gitbook.com): charges, refunds, payouts
- [Identity API](https://gitbook.com): document and bank verification
- [Connect API](https://gitbook.com): connected accounts, transfers
- ...
```
The structured Markdown lets an agent decide what to fetch in detail without loading the whole site.
### Using llms-full.txt
For a single full-context dump, `llms-full.txt` concatenates every public page in the docs into one file (~2 MB plain text). Useful when an agent has the context budget to read everything, or for offline reference materials.
## MCP — call the Evolve API as an agent
The structured-text indexes above are about *reading* the docs. If you want an agent to *call the API* directly during a session — query a charge, refund a payment, look up a customer — Evolve runs an [MCP server](../mcp/README.md) that exposes the API as agent tools.
Connecting takes about a minute and works with Claude Desktop, Cursor, Continue, Cline, and any other MCP-aware client. See [MCP → Connecting an agent](../mcp/connecting-an-agent.md).
## Best practices for agent integrations
A few patterns we've seen work well — and a couple to avoid.
<details>
<summary>Use restricted keys for agent access</summary>
If an agent calls the API on behalf of an end user, scope the key to the operations the agent should be able to perform. A read-only restricted key is the right default for assistant-style integrations; a refund-only or charge-only key is right for narrower automations.
</details>
<details>
<summary>Pin the API version</summary>
Agents calling the API should pin `Evolve-Version` explicitly. Auto-default versions can shift under you if your account upgrades; explicit pinning gives the agent reproducible behavior.
</details>
<details>
<summary>Don't let the agent generate idempotency keys</summary>
The agent can request an operation, but the idempotency key should come from your code — generated once per logical operation and persisted before the call. Otherwise a re-run of the agent will produce duplicate operations.
</details>
<details>
<summary>Surface dry-run results to the user before committing</summary>
For destructive operations (refunds, account terminations, payout cancellations), have the agent show what it's about to do and require confirmation. The Evolve API has no built-in dry-run, so the agent should describe the operation in human language before calling.
</details>
## Discoverability extras
Beyond `llms.txt`, the docs also expose:
* **`/sitemap.xml`** — full URL list, for agents that prefer XML.
* **`/.well-known/ai-plugin.json`** — OpenAPI plugin manifest pointing at our specs.
* **OpenAPI specs** — published at predictable URLs:
* <code class="expression">space.vars.docs_root</code>/openapi/payments.yaml
* <code class="expression">space.vars.docs_root</code>/openapi/identity.yaml
* <code class="expression">space.vars.docs_root</code>/openapi/connect.yaml
All four are kept in sync with the live API on every release.
## Related
* [MCP overview](../mcp/README.md) — the agent-callable side of Evolve.
* [Authentication](authentication.md) — restricted keys for scoped agent access.
* [API references](../payments-api/README.md) — for the agent to read.
references/example-site/developers/v2/getting-started/quickstart.md
---
description: >-
Make your first API call to Evolve in five minutes — in your language of
choice.
icon: rocket
---
# Quickstart
This walkthrough takes a single test charge end to end. By the time you're done, you'll have proven your environment is set up, your auth works, and a payment landed in your dashboard.
You don't need a production account or any prior payments experience to follow along.
## Prerequisites
* A test API key from [https://dashboard.test.evolve.com](https://gitbook.com) → **Developers → API keys**. It starts with `sk_test_`.
* Your language's package manager set up (`npm`, `pip`, `go get`, or `gem`).
{% hint style="warning" %}
**Treat secret keys like passwords.** Never commit them to source control or paste them into client-side code. Use environment variables.
{% endhint %}
## 1. Install the SDK
{% tabs %}
{% tab title="Node" %}
```bash
npm install @evolve/node
```
{% endtab %}
{% tab title="Python" %}
```bash
pip install evolve
```
{% endtab %}
{% tab title="Go" %}
```bash
go get github.com/evolve-pay/evolve-go
```
{% endtab %}
{% tab title="Ruby" %}
```bash
gem install evolve
```
{% endtab %}
{% tab title="cURL" %}
```bash
# No install needed.
```
{% endtab %}
{% endtabs %}
## 2. Make your first charge
{% tabs %}
{% tab title="Node" %}
```js
import Evolve from "@evolve/node";
const evolve = new Evolve(process.env.EVOLVE_SECRET_KEY);
const charge = await evolve.charges.create({
amount: 4200,
currency: "usd",
source: "tok_visa",
description: "First test payment",
}, {
idempotencyKey: crypto.randomUUID(),
});
console.log(charge.id, charge.status);
```
{% endtab %}
{% tab title="Python" %}
```python
import os, uuid
import evolve
evolve.api_key = os.environ["EVOLVE_SECRET_KEY"]
charge = evolve.Charge.create(
amount=4200,
currency="usd",
source="tok_visa",
description="First test payment",
idempotency_key=str(uuid.uuid4()),
)
print(charge.id, charge.status)
```
{% endtab %}
{% tab title="Go" %}
```go
package main
import (
"fmt"
"os"
"github.com/evolve-pay/evolve-go"
"github.com/evolve-pay/evolve-go/charge"
"github.com/google/uuid"
)
func main() {
evolve.Key = os.Getenv("EVOLVE_SECRET_KEY")
params := &evolve.ChargeParams{
Amount: evolve.Int64(4200),
Currency: evolve.String("usd"),
Source: evolve.String("tok_visa"),
Description: evolve.String("First test payment"),
}
params.SetIdempotencyKey(uuid.NewString())
ch, err := charge.New(params)
if err != nil {
panic(err)
}
fmt.Println(ch.ID, ch.Status)
}
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
require "evolve"
require "securerandom"
Evolve.api_key = ENV["EVOLVE_SECRET_KEY"]
charge = Evolve::Charge.create(
{
amount: 4200,
currency: "usd",
source: "tok_visa",
description: "First test payment",
},
idempotency_key: SecureRandom.uuid
)
puts "#{charge.id} #{charge.status}"
```
{% endtab %}
{% tab title="cURL" %}
```bash
curl https://api.test.evolve.com/v2/charges \
-H "Authorization: Bearer $EVOLVE_SECRET_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-d amount=4200 \
-d currency=usd \
-d source=tok_visa \
-d description="First test payment"
```
{% endtab %}
{% endtabs %}
A successful response looks like:
```json
{
"id": "ch_3KsM12pL9qXa7",
"object": "charge",
"amount": 4200,
"currency": "usd",
"status": "succeeded",
"created": 1714477200,
"payment_method": { "type": "card", "brand": "visa", "last4": "4242" }
}
```
## 3. Verify it landed
Open [https://dashboard.test.evolve.com/payments](https://gitbook.com). Your charge should be at the top of the list. Click it to see the request that created it, the timeline, and the webhook deliveries.
## 4. Receive a webhook
Most production integrations react to webhook events rather than polling.
{% stepper %}
{% step %}
#### Add an endpoint
In the dashboard, **Developers → Webhooks → Add endpoint**. For local testing, use a tool like [ngrok](https://ngrok.com) to expose `localhost`. Subscribe to `charge.succeeded` and `charge.failed` to start.
{% endstep %}
{% step %}
#### Verify the signature
Every event Evolve sends is HMAC-signed. Verify the signature before acting on it — see [Verifying signatures](../webhooks/verifying-signatures.md).
{% endstep %}
{% step %}
#### Replay events
Webhooks fail; that's normal. The dashboard's webhook log shows every delivery and lets you replay any event with one click. See [Retries and replay](../webhooks/retries-and-replay.md).
{% endstep %}
{% endstepper %}
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-key" style="color:$primary;">:key:</i></h4></td><td><strong>Auth deep dive</strong></td><td>Restricted keys, signature verification, rotation.</td><td><a href="authentication.md">authentication.md</a></td></tr><tr><td><h4><i class="fa-list-ul" style="color:$primary;">:list-ul:</i></h4></td><td><strong>Conventions</strong></td><td>Errors, idempotency, pagination, rate limits.</td><td><a href="conventions.md">conventions.md</a></td></tr><tr><td><h4><i class="fa-credit-card" style="color:$primary;">:credit-card:</i></h4></td><td><strong>Payments API</strong></td><td>Full reference for charges, refunds, payouts.</td><td><a href="../payments-api/">payments-api</a></td></tr></tbody></table>
references/example-site/developers/v2/getting-started/sdks.md
---
description: >-
Official SDKs for Node, Python, Go, and Ruby — plus community-maintained
libraries.
icon: cubes
---
# SDKs
Evolve maintains four official SDKs. They cover every endpoint, ship in idiomatic style for the language, and follow the same release cadence as the API itself.
## Official SDKs
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-node-js" style="color:$primary;">:node-js:</i></h4></td><td><strong>Node</strong></td><td><code>@evolve/node</code></td><td></td></tr><tr><td><h4><i class="fa-python" style="color:$primary;">:python:</i></h4></td><td><strong>Python</strong></td><td><code>evolve</code> on PyPI</td><td></td></tr><tr><td><h4><i class="fa-golang" style="color:$primary;">:golang:</i></h4></td><td><strong>Go</strong></td><td><code>github.com/evolve-pay/evolve-go</code></td><td></td></tr><tr><td><h4><i class="fa-gem" style="color:$primary;">:gem:</i></h4></td><td><strong>Ruby</strong></td><td><code>evolve</code> on RubyGems</td><td></td></tr></tbody></table>
## Install
{% tabs %}
{% tab title="Node" %}
```bash
npm install @evolve/node
# or
yarn add @evolve/node
# or
pnpm add @evolve/node
```
Requires Node 18+. TypeScript types ship in the package.
{% endtab %}
{% tab title="Python" %}
```bash
pip install evolve
# or
poetry add evolve
```
Requires Python 3.9+. Synchronous and async clients both available — use `evolve.AsyncClient` for the async path.
{% endtab %}
{% tab title="Go" %}
```bash
go get github.com/evolve-pay/evolve-go
```
Requires Go 1.21+. Strict module boundaries — every resource is a separate package (`charge`, `refund`, `payout`, etc.).
{% endtab %}
{% tab title="Ruby" %}
```ruby
# Gemfile
gem "evolve", "~> 2.0"
```
Requires Ruby 3.0+. Thread-safe; uses Net::HTTP with persistent connections.
{% endtab %}
{% endtabs %}
## What each SDK gives you
* **Full API coverage.** Every endpoint in [Payments](../payments-api/), [Identity](../identity-api/), and [Connect](../connect-api/) has a typed method.
* **Automatic retries** with exponential backoff for transient errors (5xx, 429, network errors).
* **Idempotency** — the SDK auto-generates an idempotency key for write operations unless you provide one.
* **Webhook signature verification** — `Evolve.Webhook.constructEvent` (or equivalent) handles HMAC verification.
* **Telemetry** — anonymized SDK version and request shape, used to find regressions. Disable with `EVOLVE_TELEMETRY=off`.
## Versioning
The SDK tracks the API closely. Every API version has at least one matching SDK release.
| API version | Node SDK | Python SDK | Go SDK | Ruby SDK |
| ---------------------- | -------- | ---------- | -------- | -------- |
| `2026-01-15` (default) | `2.x` | `2.x` | `v2.x.x` | `2.x` |
| `2025-07-01` | `1.x` | `1.x` | `v1.x.x` | `1.x` |
You can pin the API version in the SDK explicitly:
{% tabs %}
{% tab title="Node" %}
```js
const evolve = new Evolve(process.env.EVOLVE_SECRET_KEY, {
apiVersion: "2026-01-15",
});
```
{% endtab %}
{% tab title="Python" %}
```python
evolve.api_key = os.environ["EVOLVE_SECRET_KEY"]
evolve.api_version = "2026-01-15"
```
{% endtab %}
{% tab title="Go" %}
```go
evolve.Key = os.Getenv("EVOLVE_SECRET_KEY")
evolve.APIVersion = "2026-01-15"
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
Evolve.api_key = ENV["EVOLVE_SECRET_KEY"]
Evolve.api_version = "2026-01-15"
```
{% endtab %}
{% endtabs %}
## Source and contributions
All four SDKs are open source under the MIT license:
* [evolve-pay/evolve-node](https://github.com/GitbookIO/evolve-demo)
* [evolve-pay/evolve-python](https://github.com/GitbookIO/evolve-demo)
* [evolve-pay/evolve-go](https://github.com/GitbookIO/evolve-demo)
* [evolve-pay/evolve-ruby](https://github.com/GitbookIO/evolve-demo)
PRs welcome. For bigger changes, open an issue first to align on direction.
## Community libraries
Maintained by external contributors, not officially supported by Evolve:
* **PHP** — [`evolve-community/evolve-php`](https://github.com/evolve-community/evolve-php)
* **Java** — [`evolve-community/evolve-java`](https://github.com/evolve-community/evolve-java)
* **.NET** — [`evolve-community/Evolve.NET`](https://github.com/evolve-community/Evolve.NET)
* **Rust** — [`evolve-community/evolve-rs`](https://github.com/evolve-community/evolve-rs)
For anything not in this list, the API is plain HTTPS + JSON — `curl` and your language's HTTP client will work fine. See [Conventions](conventions.md).
## Mobile SDKs
Mobile-specific SDKs (iOS, Android, React Native) are part of the [Payments → Embedded Elements](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/accept-payments/take-a-payment) story rather than the server SDK story. They're shipped from a separate repo:
* [`evolve-pay/evolve-ios`](https://github.com/GitbookIO/evolve-demo) — Swift, supports iOS 15+.
* [`evolve-pay/evolve-android`](https://github.com/GitbookIO/evolve-demo) — Kotlin, supports Android 8+.
* [`evolve-pay/evolve-react-native`](https://github.com/GitbookIO/evolve-demo) — TypeScript wrapper around both.
These handle PCI-scope-reducing card collection on the client. Don't use a server SDK from a mobile app — it'd require shipping a secret key.
references/example-site/developers/v2/identity-api/README.md
---
icon: id-card
description: Verification sessions, documents, bank checks, and business verification.
---
# Identity API
The Identity API runs the full verification stack — document review, selfie liveness, bank account verification, and business KYB. The operation reference is auto-generated and listed below this page in the sidebar.
## Resources
* **Verification sessions** — top-level resource, one per identity, bank, or business verification.
* **Documents** — captured documents and their per-check results.
* **Bank verifications** — Plaid-instant or micro-deposits flow records.
## How a verification flows
```mermaid
flowchart LR
A[POST /verification_sessions] --> B[Hosted URL returned]
B --> C[Customer completes flow]
C --> D[Webhook fires]
D --> E[GET /verification_sessions/:id]
```
1. **Your server creates a session** — pass `type` (identity/bank/business) and `customer`. Get back a session record with a `url` to send the customer to.
2. **Customer completes the hosted flow** — Evolve handles all the capture and review.
3. **Webhook fires when complete** — `verification_session.verified`, `.failed`, or `.manual_review`. See [Event catalog](../webhooks/event-catalog.md).
4. **You retrieve the result** — the full check breakdown is on the session.
You can also drive the flow programmatically — submit documents, run individual checks, override decisions. Those operations are in the auto-generated reference.
## A minimal example
{% tabs %}
{% tab title="Node" %}
```js
const session = await evolve.identity.verificationSessions.create({
type: "identity",
customer: "cus_4n2P3qR5sT6uV",
return_url: "https://yourapp.com/verified",
});
// Send `session.url` to the customer.
console.log(session.url);
```
{% endtab %}
{% tab title="Python" %}
```python
session = evolve.VerificationSession.create(
type="identity",
customer="cus_4n2P3qR5sT6uV",
return_url="https://yourapp.com/verified",
)
print(session.url)
```
{% endtab %}
{% tab title="cURL" %}
```bash
curl https://api.evolve.com/v2/verification_sessions \
-H "Authorization: Bearer $EVOLVE_SECRET_KEY" \
-d type=identity \
-d customer=cus_4n2P3qR5sT6uV \
-d return_url=https://yourapp.com/verified
```
{% endtab %}
{% endtabs %}
## Conceptual background
For the product-side concepts — when to verify, which method to pick, what the customer sees — see the [Identity product space](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/).
references/example-site/developers/v2/mcp/connecting-an-agent.md
---
icon: plug-circle-bolt
description: Step-by-step setup for Claude Desktop, Cursor, Continue, Cline, and any MCP-aware client.
---
# Connecting an agent
Setup is the same for every MCP client — point it at Evolve's MCP server URL and provide an API key. The specifics of where to put that config differ per client.
## The Evolve MCP server URL
```
https://mcp.evolve.com
```
The server speaks the Streamable HTTP transport (the standard MCP HTTP transport since spec version 2025-03-26). Use a restricted API key for the agent — see [Security](README.md#security-and-permissions).
{% hint style="info" %}
**Test mode:** point at `https://mcp.test.evolve.com` and use an `sk_test_` key. The set of available tools is identical; the data they return is from your test environment.
{% endhint %}
## Connect from Claude Desktop
Open `~/Library/Application Support/Claude/claude_desktop_config.json` (or **Settings → Developer → Edit Config**) and add:
```json
{
"mcpServers": {
"evolve": {
"url": "https://mcp.evolve.com",
"headers": {
"Authorization": "Bearer rk_live_..."
}
}
}
}
```
Restart Claude Desktop. The Evolve tools appear in the new-chat tool picker as `evolve__*`.
## Connect from Cursor
In Cursor's **Settings → MCP**, add a new server:
* **Name:** `evolve`
* **Type:** `http`
* **URL:** `https://mcp.evolve.com`
* **Headers:** `Authorization: Bearer rk_live_...`
Cursor's agent panel will pick the tools up automatically.
## Connect from Continue
In `~/.continue/config.json`:
```json
{
"mcpServers": [
{
"name": "evolve",
"url": "https://mcp.evolve.com",
"headers": {
"Authorization": "Bearer rk_live_..."
}
}
]
}
```
## Connect from Cline (VS Code)
Open the Cline panel, click the MCP icon, **Configure MCP servers**:
```json
{
"mcpServers": {
"evolve": {
"url": "https://mcp.evolve.com",
"headers": { "Authorization": "Bearer rk_live_..." }
}
}
}
```
## Verifying the connection
After connecting, ask the agent:
> List the tools you have for Evolve.
You should see something like:
```
- evolve_search_customers
- evolve_get_charge
- evolve_create_refund
- evolve_get_verification_session
- evolve_search_docs
- ...
```
If the list is empty, the most common causes are a typo in the URL, a missing/expired API key, or the client not having loaded the new config (restart usually fixes this).
## Available tools
A non-exhaustive list of what the server exposes today. Each tool has typed parameters that mirror the corresponding API endpoint.
### Read tools
| Tool | Description |
| --- | --- |
| `evolve_search_customers` | Find a customer by id, email, or metadata key/value. |
| `evolve_get_customer` | Retrieve a customer by id, including saved payment methods. |
| `evolve_search_charges` | Search charges by customer, amount range, status, or time. |
| `evolve_get_charge` | Retrieve a charge by id with the full event timeline. |
| `evolve_search_refunds` | Search refunds. |
| `evolve_get_payout` | Retrieve a payout including its line items. |
| `evolve_get_balance` | Current available, pending, and reserved balance per currency. |
| `evolve_get_verification_session` | Retrieve an Identity verification session and its check results. |
| `evolve_get_connected_account` | Retrieve a Connect connected account. |
| `evolve_search_docs` | Query the public docs and return ranked text snippets. |
| `evolve_get_event` | Retrieve a webhook event by id, with delivery history. |
### Write tools (gated by key permissions)
| Tool | Description |
| --- | --- |
| `evolve_create_refund` | Issue a refund (full or partial) against a charge. |
| `evolve_capture_charge` | Capture an authorized charge. |
| `evolve_void_charge` | Void an authorized charge before capture. |
| `evolve_replay_webhook_event` | Replay a webhook event to its endpoint. |
| `evolve_pause_webhook_endpoint` | Pause a webhook endpoint. |
If a tool isn't in your key's scope, it shows up in the list with a `[restricted]` tag and the agent can see it exists but gets a permission error if it tries to call it.
## Approval and confirmation
Every MCP-aware client we've tested prompts the human before each tool call. We strongly recommend keeping that on for write tools — the audit log will show every call, but a "the agent decided to refund $5,000" surprise is not the discovery you want.
For read-only tools, most teams turn off the approval prompt to keep flow smooth. That's safe.
## Tool-call audit log
Every MCP tool call is logged to your audit log just like a regular API call. Filter by `actor_type: mcp_session` to see only agent activity:
* The session id (one per MCP connection).
* The tool name and the parameters it was called with.
* The user who's signed into the MCP client (where the client supports passing this).
* The response.
This is the trail your security team will want for any non-trivial agent-driven work.
## Limits
* **Per-session rate limit:** 60 tool calls per minute. Burst above that and the next tool call returns a friendly error suggesting the agent slow down.
* **Per-tool rate limits** apply on top of the per-session limit, mirroring the API rate limits.
* **Session lifetime:** 8 hours, after which the agent reconnects automatically.
## Troubleshooting
<details>
<summary>"The tool returns a 401"</summary>
API key missing, wrong, or revoked. Check the `Authorization` header in your client config; rotate the key if you suspect leakage.
</details>
<details>
<summary>"The agent calls a tool but says it timed out"</summary>
The MCP server has a 30-second per-tool timeout. Some search tools (especially `evolve_search_docs`) can be slow on cold cache. Retry usually works.
</details>
<details>
<summary>"I want to expose only specific tools to the agent"</summary>
Use a restricted API key scoped to just the resources you want accessible. The MCP server reflects the key's permissions automatically — tools the key can't perform don't appear in the list.
</details>
## Related
* [MCP overview](README.md) — what MCP gives you.
* [Authentication → Restricted keys](../getting-started/authentication.md#restricted-keys) — scoping the key.
* [For AI agents](../getting-started/for-ai-agents.md) — the docs-reading companion.
references/example-site/developers/v2/mcp/README.md
---
description: >-
Connect an AI agent to Evolve via Model Context Protocol — query the API, take
actions, get docs context.
icon: plug
---
# Overview
Evolve runs a hosted [Model Context Protocol](https://modelcontextprotocol.io) server that exposes the Evolve API as a set of tools an AI agent can call directly. Connect once and your agent — Claude Desktop, Cursor, Continue, Cline, or any MCP-aware client — can read your account, look up customers, refund payments, and inspect verification sessions during a conversation.
This is for the _interactive_ side of agent integrations. For agents that just need to read the docs, see [llms.txt and llms-full.txt](../getting-started/for-ai-agents.md).
## What MCP gives you
When connected, your agent can:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-magnifying-glass" style="color:$primary;">:magnifying-glass:</i></h4></td><td><strong>Look things up</strong></td><td>Find a customer, charge, payout, or verification by id, email, or amount.</td><td></td></tr><tr><td><h4><i class="fa-bolt-lightning" style="color:$primary;">:bolt-lightning:</i></h4></td><td><strong>Take actions</strong></td><td>Issue refunds, capture authorizations, retry failed payouts, replay webhooks.</td><td></td></tr><tr><td><h4><i class="fa-book-open" style="color:$primary;">:book-open:</i></h4></td><td><strong>Pull context from the docs</strong></td><td>The same docs you're reading, queryable by the agent for grounded answers.</td><td></td></tr></tbody></table>
## What it doesn't do
* **It's not autonomous.** The agent calls tools on your behalf, with your approval (most clients prompt before each tool call). Evolve doesn't run an agent of its own.
* **It's not a replacement for the API.** Production code should call the API directly with an SDK. MCP is for human-in-the-loop debugging, customer support, and assistant-style workflows.
* **It doesn't bypass your security.** Tools are scoped to the API key you connect with. A read-only key gives the agent read-only abilities, full stop.
## Quick example — what a session looks like
A support agent in Claude Desktop, after connecting Evolve via MCP:
> **You:** Customer at jordan@acme.com is asking why their last payment failed. What happened?
>
> **Claude:** \[Calls `evolve_search_customers` tool] Found one match — `cus_4n2P3qR5sT6uV`. Their most recent payment was `ch_8f2K3pL9qX` for $128.00 on April 28, which **failed with `card_declined` (decline\_code: insufficient\_funds)**. The card was the one they used successfully twice before. Want me to draft an email asking them to try another card?
The agent did two things: searched for the customer, then retrieved the failed charge — without you leaving the chat or opening the dashboard.
## Connect
Setup takes about a minute, no install required.
<a href="connecting-an-agent.md" class="button primary">Set it up</a>
## Available tools
The MCP server exposes one tool per common operation. The full list is at [Connecting an agent → Available tools](connecting-an-agent.md#available-tools); a representative slice:
| Tool | What it does |
| --------------------------------- | ---------------------------------------------------------- |
| `evolve_search_customers` | Find a customer by id, email, or metadata. |
| `evolve_get_charge` | Fetch a charge by id, with full timeline. |
| `evolve_create_refund` | Issue a refund (full or partial). |
| `evolve_get_verification_session` | Inspect an Identity verification result. |
| `evolve_search_docs` | Query the public Evolve docs and get back ranked snippets. |
## Security and permissions
The MCP server authenticates with an Evolve API key. We strongly recommend a [restricted key](../getting-started/authentication.md#restricted-keys) scoped to the minimum permissions the agent needs.
For interactive support work, a typical scoping:
* **Read** on charges, customers, refunds, payouts, verifications.
* **Write** on refunds (so the agent can issue them with your approval).
* **No write** on anything else (no creating charges, no creating connected accounts, no rolling keys).
Every tool call is logged to your [audit log](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/compliance/audit-logs) just like a regular API call, with a special `actor_type: mcp_session` so you can filter for agent activity.
## Related
* [Connecting an agent](connecting-an-agent.md) — step-by-step setup for Claude, Cursor, and others.
* [For AI agents](../getting-started/for-ai-agents.md) — the docs-side companion (`llms.txt`).
* [Authentication → Restricted keys](../getting-started/authentication.md#restricted-keys) — scoping access.
references/example-site/developers/v2/payments-api/README.md
---
description: Charges, refunds, payouts, and balance — the stable v2 surface.
icon: credit-card
---
# Overview
The Payments API is the workhorse of Evolve — taking money, refunding money, and moving it to your bank. The operation reference is auto-generated and listed below this page in the sidebar.
{% hint style="success" icon="circle-check" %}
**You're viewing v2 — the stable default.** Use the variant dropdown in the top bar to switch to **v1** (deprecated) or **v3** (preview).
{% endhint %}
## Base URL
```
https://api.evolve.com/v2
```
The default version date for v2 is <code class="expression">space.vars.api_version</code>. Pin it in your code with the `Evolve-Version` header — see [Authentication → API versioning](../getting-started/authentication.md#api-versioning).
## Resources
* **Charges** — create, capture, void, retrieve, list.
* **Refunds** — full and partial refunds against a charge.
* **Payouts** — list and retrieve. Schedule is configured in the dashboard.
* **Balance** — available, pending, and reserved balances per currency.
## What changed since v1
If you're migrating from v1, the most consequential changes:
* `POST /sources/charge` is gone. Use `POST /charges` directly — the source-type is inferred from the token.
* Error `code` values are stable across languages and won't change within v2.
* Webhook signatures use HMAC-SHA256 (v1 used HMAC-SHA1). See [Verifying signatures](../webhooks/verifying-signatures.md).
* Idempotency keys are required on every write endpoint, not optional.
The full migration walkthrough lives at [Guides → v1 → v2 migration](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/migrate-payments-v1-to-v2). v1 sunset is **2026-12-31**.
## What's coming in v3
v3 is in preview today. Use the variant dropdown to flip over and explore. Highlights:
* `Charge` is renamed to `Payment`.
* `capture: true|false` becomes `capture_method: automatic|manual`.
* Authorization window extended from 7 to 30 days.
* Multi-currency capture — authorize in USD, capture in EUR at wholesale FX.
When v3 graduates to stable, we'll publish a migration tool. For now, treat v3 as exploratory.
## Conceptual background
For the product-side concepts behind these endpoints — what a charge actually does, how settlement timing works, when 3-D Secure applies — see the [Payments product space](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/). It's the right complement to the API reference.
references/example-site/developers/v2/README.md
---
description: Build on Evolve — APIs, SDKs, webhooks, and AI-agent access for every product.
icon: code
cover: .gitbook/assets/developers-cover.png
coverY: 0
layout:
width: wide
cover:
visible: true
size: full
title:
visible: true
description:
visible: true
tableOfContents:
visible: true
outline:
visible: true
pagination:
visible: true
metadata:
visible: true
tags:
visible: true
---
# Developers
{% columns %}
{% column width="50%" %}
Build with Evolve. The Developers space is the source of truth for APIs, SDKs, webhooks, and the agent integrations across all three products — [Payments](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/), [Identity](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/), and [Connect](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/).
**Looking for product workflows and concepts?** Those live in the product spaces — this space is the technical reference.
{% endcolumn %}
{% column width="50%" %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This space demonstrates **variants** — three Git-Synced spaces (`v1`, `v2`, `v3`) appear as a single dropdown in the top bar. Switch between them to see how the Payments API differs across versions. The Reference pages are **auto-rendered from OpenAPI specs** in `developers/openapi/v2/`. Code samples on the Quickstart and Authentication pages use the **tabs block** for Node, Python, Go, Ruby, and cURL.
{% endhint %}
{% endcolumn %}
{% endcolumns %}
## Pick your path
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-rocket" style="color:$primary;">:rocket:</i></h4></td><td><strong>Quickstart</strong></td><td>Make your first API call in under five minutes.</td><td><a href="getting-started/quickstart.md">quickstart.md</a></td></tr><tr><td><h4><i class="fa-key" style="color:$primary;">:key:</i></h4></td><td><strong>Authentication</strong></td><td>Keys, scopes, and signature verification.</td><td><a href="getting-started/authentication.md">authentication.md</a></td></tr><tr><td><h4><i class="fa-cubes" style="color:$primary;">:cubes:</i></h4></td><td><strong>SDKs</strong></td><td>Node, Python, Go, Ruby — official and idiomatic.</td><td><a href="getting-started/sdks.md">sdks.md</a></td></tr><tr><td><h4><i class="fa-list-ul" style="color:$primary;">:list-ul:</i></h4></td><td><strong>Conventions</strong></td><td>Errors, idempotency, pagination, rate limits.</td><td><a href="getting-started/conventions.md">conventions.md</a></td></tr></tbody></table>
## API references
Each product has its own auto-generated reference. Endpoints, parameters, response shapes, and an interactive **Test it** panel for every operation.
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-credit-card" style="color:$primary;">:credit-card:</i></h4></td><td><strong>Payments API</strong></td><td>Charges, refunds, payouts, balance.</td><td><a href="payments-api/">payments-api</a></td></tr><tr><td><h4><i class="fa-id-card" style="color:$primary;">:id-card:</i></h4></td><td><strong>Identity API</strong></td><td>Verification sessions, documents, bank checks.</td><td><a href="identity-api/">identity-api</a></td></tr><tr><td><h4><i class="fa-circles-overlap" style="color:$primary;">:circles-overlap:</i></h4></td><td><strong>Connect API</strong></td><td>Connected accounts, transfers, checkout sessions.</td><td><a href="connect-api/">connect-api</a></td></tr><tr><td><h4><i class="fa-bolt" style="color:$primary;">:bolt:</i></h4></td><td><strong>Webhooks</strong></td><td>Event catalog and signature verification.</td><td><a href="webhooks/">webhooks</a></td></tr></tbody></table>
## For AI agents
Evolve publishes structured indexes for AI agents and code copilots — `llms.txt` and `llms-full.txt` — so an agent landing on these docs can navigate efficiently or load the whole content set into context. We also expose Evolve itself as an [MCP server](mcp/) so an agent can call the API directly during a session.
<a href="getting-started/for-ai-agents.md" class="button secondary">Agent integration guide</a> <a href="mcp/" class="button secondary">MCP server</a>
## Synced with GitHub
These docs are bidirectionally synced with the [evolve-pay/docs](https://github.com/GitbookIO/evolve-demo) repository. Edits in the GitBook editor flow back to GitHub as commits; changes pushed to the repo (including PRs from contributors) propagate to the published docs after merge. Contribute via PR or via the **Edit in GitHub** badge in the page header.
## Conventions at a glance
A short version of what's covered in detail under [Conventions](getting-started/conventions.md):
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><h3><i class="fa-globe" style="color:$primary;">:globe:</i> <strong>Base URLs</strong></h3></td><td>Live: <code class="expression">space.vars.api_live</code><br>Test: <code class="expression">space.vars.api_test</code></td></tr><tr><td><h3><i class="fa-tag" style="color:$primary;">:tag:</i> <strong>Versioning</strong></h3></td><td>Date-pinned via the <code>Evolve-Version</code> header. Default: <code class="expression">space.vars.api_version</code>. Major shape changes ship as variants — <code>v1</code>, <code>v2</code>, <code>v3</code>.</td></tr><tr><td><h3><i class="fa-list" style="color:$primary;">:list:</i> <strong>Pagination</strong></h3></td><td>Cursor-based. 100 per page default, 1000 max. <code>has_more</code> and <code>next_cursor</code> on every list.</td></tr><tr><td><h3><i class="fa-gauge-high" style="color:$primary;">:gauge-high:</i> <strong>Rate limits</strong></h3></td><td>500 requests/second on Growth, custom on Enterprise. <code>X-RateLimit-*</code> headers on every response.</td></tr></tbody></table>
## Get help
{% columns %}
{% column width="50%" %}
#### Talk to support
For account-specific questions, integration help, or production incidents, open a ticket from the dashboard.
<a href="https://gitbook.com" class="button primary">Open a ticket</a>
{% endcolumn %}
{% column width="50%" %}
#### Status and changelog
Real-time platform status and the running list of API changes.
<a href="https://gitbook.com" class="button secondary">Status page</a> <a href="https://app.gitbook.com/s/ErQsbFsgm6eg9BApdmPl/" class="button secondary">Changelog</a>
{% endcolumn %}
{% endcolumns %}
references/example-site/developers/v2/SUMMARY.md
# Table of contents
* [Developers](README.md)
## Getting started
* [Quickstart](getting-started/quickstart.md)
* [Authentication](getting-started/authentication.md)
* [SDKs](getting-started/sdks.md)
* [Conventions](getting-started/conventions.md)
* [For AI agents](getting-started/for-ai-agents.md)
## Payments API
* [Overview](payments-api/README.md)
* ```yaml
type: builtin:openapi
props:
models: false
downloadLink: true
dependencies:
spec:
ref:
kind: openapi
spec: evolve-payments-v2
```
## Identity API
* [Overview](identity-api/README.md)
* ```yaml
type: builtin:openapi
props:
models: false
downloadLink: true
dependencies:
spec:
ref:
kind: openapi
spec: evolve-identity-v2
```
## Connect API
* [Overview](connect-api/README.md)
* ```yaml
type: builtin:openapi
props:
models: false
downloadLink: true
dependencies:
spec:
ref:
kind: openapi
spec: evolve-connect-v2
```
## Webhooks
* [Overview](webhooks/README.md)
* [Verifying signatures](webhooks/verifying-signatures.md)
* [Event catalog](webhooks/event-catalog.md)
* [Retries and replay](webhooks/retries-and-replay.md)
## MCP
* [Overview](mcp/README.md)
* [Connecting an agent](mcp/connecting-an-agent.md)
references/example-site/developers/v2/webhooks/event-catalog.md
---
icon: list
description: Every event type Evolve emits, grouped by product.
---
# Event catalog
This page lists every event type Evolve emits, with a one-line description of when it fires. The full payload schema for each event matches the corresponding object in the API reference.
## Payments events
| Event | Fires when |
| --- | --- |
| `charge.pending` | A charge has been created and is being processed. Most teams skip this. |
| `charge.succeeded` | A charge captured successfully. The most common event. |
| `charge.failed` | A charge was declined or hit a processing error. `decline_code` on the data. |
| `charge.captured` | An authorized charge was captured. Only fires for two-step charges. |
| `charge.voided` | An authorization was released without being captured. |
| `charge.refunded` | A refund was successfully issued against the charge. |
| `charge.disputed` | A cardholder filed a chargeback against the charge. |
| `refund.created` | A refund was issued (mirrors `charge.refunded` from a refund-centric perspective). |
| `refund.succeeded` | A refund was accepted by the network and the funds are returning. |
| `refund.failed` | A refund failed (rare; usually means the cardholder's account is closed). |
| `dispute.created` | A new dispute was opened. |
| `dispute.evidence_required` | Reminder that the dispute response window is closing. |
| `dispute.won` | The network ruled in your favor. Funds are returned. |
| `dispute.lost` | The network ruled against you. Funds and fee remain with the cardholder. |
| `payout.created` | A payout was scheduled. |
| `payout.paid` | The payout completed and funds are in the bank. |
| `payout.failed` | The payout failed (closed account, frozen account). |
## Identity events
| Event | Fires when |
| --- | --- |
| `verification_session.created` | A new verification session was created. |
| `verification_session.processing` | The customer has submitted; Evolve is reviewing. |
| `verification_session.verified` | All required checks passed. |
| `verification_session.failed` | One or more required checks failed. Reason on the data. |
| `verification_session.manual_review` | An automated check was inconclusive; a human is reviewing. |
| `verification_session.expired` | The customer didn't complete within the window. |
| `bank_verification.verified` | A bank account was verified. |
| `bank_verification.failed` | A bank verification failed (Plaid auth, micro-deposits mismatch). |
| `screening.match_added` | Ongoing monitoring detected a new sanctions/PEP/adverse-media match. |
## Connect events
| Event | Fires when |
| --- | --- |
| `account.created` | A new connected account was created. |
| `account.verified` | A connected account completed onboarding and is enabled for charges and payouts. |
| `account.requirements_updated` | Evolve needs more information from this account (additional document, etc.). |
| `account.restricted` | The account has been restricted (typically risk-related). |
| `transfer.created` | A transfer to a connected account was initiated. |
| `transfer.paid` | A transfer settled in the connected account's balance. |
| `transfer.reversed` | A transfer was reversed (typically alongside a refund). |
| `application_fee.created` | An application fee was earned by the platform. |
| `application_fee.refunded` | An application fee was returned to the seller (e.g. as part of a refund). |
## Routing events
| Event | Fires when |
| --- | --- |
| `routing.acquirer_degraded` | An acquirer's success rate has dropped below the failover threshold. |
| `routing.acquirer_recovered` | A previously degraded acquirer is healthy again. |
These fire regardless of whether failover is enabled — useful even as monitoring signals. *Enterprise plans only.*
## Subscribing to events
Configure subscriptions per endpoint under **Developers → Webhooks → [endpoint] → Events**. You can subscribe to:
* **Specific events** (recommended) — list the events you handle.
* **An entire resource** (e.g. `charge.*`) — wildcards supported.
* **Everything** (`*`) — discouraged in production.
The dashboard shows the list of events delivered to each endpoint over the last 30 days, so you can quickly see what you're actually receiving.
## Payload version
Every event includes the `api_version` it was generated against:
```json
{ "id": "evt_...", "type": "charge.succeeded", "api_version": "2026-01-15", ... }
```
Webhook payload shapes change with API versions, just like API responses. The version on the event matches the version configured on the webhook endpoint (overridable per endpoint, in case you want to test a new API version on a specific webhook before rolling it out everywhere).
## Related
* [Verifying signatures](verifying-signatures.md) — verify before parsing.
* [Retries and replay](retries-and-replay.md) — handling delivery failures.
* [Conventions → Object IDs](../getting-started/conventions.md#object-ids) — what each prefix means.
references/example-site/developers/v2/webhooks/README.md
---
description: >-
How Evolve sends events to your endpoints, how to verify them, and how to
handle failures.
icon: bolt
---
# Overview
Webhooks let Evolve push events to your servers as they happen — a charge succeeded, a verification finished, a dispute opened. Most production integrations rely on webhooks rather than polling, because polling at the granularity needed for payments hits the rate limits fast.
## What an event looks like
```json
{
"id": "evt_3KsM12pL9qXa7",
"object": "event",
"type": "charge.succeeded",
"created": 1714477200,
"api_version": "2026-01-15",
"data": {
"object": {
"id": "ch_3KsM12pL9qXa7",
"object": "charge",
"amount": 4200,
"currency": "usd",
"status": "succeeded"
}
},
"request": {
"id": "req_8h2nF6m4Lp",
"idempotency_key": "7b8f2c4a-91d2-4fe1-9b6a-2c8e5f1a9b0c"
}
}
```
Every event has the same outer shape — `id`, `type`, `data.object` — regardless of which resource it's about.
## Setting up an endpoint
{% stepper %}
{% step %}
#### Add the endpoint
In the dashboard, **Developers → Webhooks → Add endpoint**. Enter your URL and pick the events to subscribe to. For local testing, expose `localhost` with [ngrok](https://ngrok.com) or similar.
{% endstep %}
{% step %}
#### Save the signing secret
Each endpoint has its own signing secret (prefix `whsec_`). Copy it and add it to your environment as `EVOLVE_WEBHOOK_SECRET`.
{% endstep %}
{% step %}
#### Verify the signature
In your handler, verify the `Evolve-Signature` header before parsing the body. See [Verifying signatures](verifying-signatures.md) for code in every supported language.
{% endstep %}
{% step %}
#### Respond with 2xx
Evolve treats any `2xx` as a successful delivery. Anything else is retried — see [Retries and replay](retries-and-replay.md).
{% endstep %}
{% endstepper %}
## What's in the rest of the section
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h4></td><td><strong>Verifying signatures</strong></td><td>Per-language signature verification.</td><td><a href="verifying-signatures.md">verifying-signatures.md</a></td></tr><tr><td><h4><i class="fa-list" style="color:$primary;">:list:</i></h4></td><td><strong>Event catalog</strong></td><td>Every event type Evolve emits, with payload examples.</td><td><a href="event-catalog.md">event-catalog.md</a></td></tr><tr><td><h4><i class="fa-rotate" style="color:$primary;">:rotate:</i></h4></td><td><strong>Retries and replay</strong></td><td>Retry behavior and how to replay missed events.</td><td><a href="retries-and-replay.md">retries-and-replay.md</a></td></tr></tbody></table>
## Best practices
A few patterns that consistently save trouble:
<details>
<summary>Be idempotent on your side too</summary>
Webhooks can be delivered more than once — at-least-once delivery, not exactly-once. Use the event `id` as a dedup key in your database; if you've seen the id before, ack and skip the work.
</details>
<details>
<summary>Respond fast, defer the work</summary>
Acknowledge the webhook (return 2xx) within a few seconds. If the actual processing is slow, push the event onto a queue and process it asynchronously. Slow handlers cause Evolve to retry, which causes duplicate processing.
</details>
<details>
<summary>Don't trust the event payload more than necessary</summary>
For destructive operations, refetch the canonical object from the API after receiving the event. Webhook payloads are point-in-time — by the time you process the event, the object may have changed.
</details>
<details>
<summary>Subscribe narrowly</summary>
Subscribe only to event types you actually handle. Subscribing to `*` is convenient but means more retries and more noise during incidents.
</details>
references/example-site/developers/v2/webhooks/retries-and-replay.md
---
description: >-
How Evolve retries failed deliveries, and how to replay events your handler
missed.
icon: rotate
---
# Retries and replay
Webhook delivery isn't perfect. Your server will go down, your handler will throw, networks will glitch. Evolve treats this as the default case rather than the exception — every delivery is retried until it succeeds or we give up after **3 days**.
## The retry schedule
If your endpoint returns anything other than `2xx`, or doesn't respond within 30 seconds, Evolve retries:
| Attempt | After |
| ---------- | ----------- |
| 1 | Immediately |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 15 minutes |
| 5 | 1 hour |
| 6 | 4 hours |
| 7 | 16 hours |
| 8 | 1 day |
| 9 | 2 days |
| 10 (final) | 3 days |
After the final attempt, the event is marked **failed** and stays in the dashboard's webhook log for 90 days. You can replay it manually any time within that window.
## What counts as a successful delivery
Anything in the `2xx` range. Redirects (`3xx`) are followed up to 5 hops. Anything else (including timeouts) triggers retry.
We strongly recommend returning `2xx` quickly (under a few seconds) and processing the event asynchronously. If your handler does the actual work synchronously and takes 25 seconds, you're one slow database query away from a `504` and a duplicate-delivery storm during retries.
## Replaying events from the dashboard
The webhook log shows every delivery attempt for every endpoint:
{% stepper %}
{% step %}
#### Find the event
**Developers → Webhooks → \[endpoint] → Events**. Filter by event type, status, or time range. Each row shows the delivery attempts and the response code Evolve got.
{% endstep %}
{% step %}
#### Replay it
Click an event and hit **Replay**. Evolve sends a fresh delivery to the same endpoint, with a current timestamp on the signature so it passes a strict freshness check.
{% endstep %}
{% step %}
#### Watch the new attempt
The dashboard shows the new delivery attempt and your response within seconds.
{% endstep %}
{% endstepper %}
You can also replay in bulk — select multiple events and click **Replay all** to re-deliver in chronological order. This is the right tool after fixing a handler bug that caused a backlog of failed deliveries.
## Replaying programmatically
For automated recovery (e.g. a deploy script that replays events from the deployment window), use the API:
```http
POST /v2/webhook_endpoints/{endpoint_id}/events/{event_id}/replay
Authorization: Bearer sk_live_...
```
Returns the new delivery attempt's `id`. Replays are rate-limited per endpoint to 100/minute.
## Why deliveries fail (and what to do)
<details>
<summary>Connection refused / timeout</summary>
Your endpoint is down. Investigate why. Evolve will keep retrying for 3 days, so a brief outage doesn't lose events — but if you discover a long outage, replay manually to be sure.
</details>
<details>
<summary>500 Internal Server Error</summary>
Your handler threw. Check your logs for the underlying error. Common causes: parsing the body as JSON before verifying the signature (which fails on the raw bytes), missing environment variables, downstream service errors.
</details>
<details>
<summary>400 Bad Request (with "Invalid signature" message)</summary>
Signature verification failed. See [Verifying signatures → Common pitfalls](verifying-signatures.md#common-pitfalls).
</details>
<details>
<summary>Slow responses (mostly 504 from gateway)</summary>
Your handler is too slow. Acknowledge fast and queue the actual work. Even if your processing typically completes in 5 seconds, the p99 will eventually exceed 30 and trigger retries.
</details>
## Idempotency on your side
Webhook delivery is **at-least-once**, not exactly-once. The same event can be delivered multiple times if:
* Evolve retried after your endpoint timed out, but the original request actually completed on your side.
* You replayed an event manually.
* A network glitch caused us to think delivery failed when it didn't.
Use the event `id` (`evt_...`) as a dedup key in your database. If you've seen the id before, ack and skip the work. The id is stable across retries and replays.
```sql
CREATE TABLE processed_webhook_events (
event_id TEXT PRIMARY KEY,
processed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```
## Pause an endpoint
Sometimes you need to stop receiving events temporarily — during a long outage, a deploy with breaking changes, an investigation. **Pause** an endpoint in the dashboard and Evolve stops trying to deliver to it. Events queue up; when you **Resume**, Evolve delivers them in order, with the original timestamps preserved on the signature payload.
There's a 7-day cap on paused-endpoint queues. After 7 days, queued events are dropped (but still visible for manual replay in the webhook log).
## Related
* [Verifying signatures](verifying-signatures.md) — verify before processing.
* [Event catalog](event-catalog.md) — every event type.
* [Conventions → Idempotency](../getting-started/conventions.md#idempotency) — for outbound requests, the same idea.
references/example-site/developers/v2/webhooks/verifying-signatures.md
---
icon: shield-halved
description: HMAC-SHA256 signature verification — in every supported language.
---
# Verifying signatures
Every webhook delivery includes an `Evolve-Signature` header. The signature is HMAC-SHA256 over the timestamp + the raw request body, signed with the endpoint's signing secret. Verify it before doing anything else with the request.
## The header format
```
Evolve-Signature: t=1714477200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
```
* `t` — Unix timestamp (seconds) when Evolve generated the signature.
* `v1` — the HMAC-SHA256 signature.
## The verification algorithm
```
signed_payload = "{t}.{raw_body}"
expected = HMAC_SHA256(signing_secret, signed_payload)
valid = constant_time_compare(expected, v1)
```
Three things to enforce:
1. **The signature matches.** Use a constant-time comparison.
2. **The timestamp is recent.** Reject anything older than 5 minutes (default tolerance).
3. **You're using the raw body.** Re-serializing JSON before verification breaks the signature.
## Verifying in code
The official SDKs ship a one-call helper. Use it.
{% tabs %}
{% tab title="Node" %}
```js
import express from "express";
import Evolve from "@evolve/node";
const evolve = new Evolve(process.env.EVOLVE_SECRET_KEY);
const app = express();
// IMPORTANT: get the raw body, not parsed JSON.
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["evolve-signature"];
let event;
try {
event = evolve.webhooks.constructEvent(
req.body,
sig,
process.env.EVOLVE_WEBHOOK_SECRET
);
} catch (err) {
console.warn("Bad signature:", err.message);
return res.status(400).send("Invalid signature");
}
// Handle the event.
switch (event.type) {
case "charge.succeeded": handleCharge(event.data.object); break;
case "charge.failed": handleFailure(event.data.object); break;
}
res.json({ received: true });
});
```
{% endtab %}
{% tab title="Python" %}
```python
from flask import Flask, request, jsonify
import evolve, os
evolve.api_key = os.environ["EVOLVE_SECRET_KEY"]
app = Flask(__name__)
@app.route("/webhook", methods=["POST"])
def webhook():
payload = request.get_data(as_text=False) # raw bytes, not parsed JSON
sig = request.headers.get("Evolve-Signature")
try:
event = evolve.Webhook.construct_event(
payload, sig, os.environ["EVOLVE_WEBHOOK_SECRET"]
)
except evolve.error.SignatureVerificationError:
return "Invalid signature", 400
if event.type == "charge.succeeded":
handle_charge(event.data.object)
return jsonify(received=True)
```
{% endtab %}
{% tab title="Go" %}
```go
package main
import (
"io"
"net/http"
"os"
"github.com/evolve-pay/evolve-go/webhook"
)
func handler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "read failed", 400)
return
}
event, err := webhook.ConstructEvent(
body,
r.Header.Get("Evolve-Signature"),
os.Getenv("EVOLVE_WEBHOOK_SECRET"),
)
if err != nil {
http.Error(w, "invalid signature", 400)
return
}
switch event.Type {
case "charge.succeeded":
// ...
}
w.Write([]byte(`{"received":true}`))
}
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
require "sinatra"
require "evolve"
post "/webhook" do
payload = request.body.read
sig = request.env["HTTP_EVOLVE_SIGNATURE"]
begin
event = Evolve::Webhook.construct_event(
payload, sig, ENV["EVOLVE_WEBHOOK_SECRET"]
)
rescue Evolve::SignatureVerificationError
halt 400, "Invalid signature"
end
case event.type
when "charge.succeeded"
handle_charge(event.data.object)
end
status 200
{ received: true }.to_json
end
```
{% endtab %}
{% tab title="Manual (any language)" %}
```python
import hmac, hashlib, time
def verify(payload: bytes, sig_header: str, secret: str, tolerance: int = 300):
parts = dict(p.split("=", 1) for p in sig_header.split(","))
timestamp = int(parts["t"])
# Reject stale signatures.
if abs(time.time() - timestamp) > tolerance:
raise ValueError("timestamp too old")
signed = f"{timestamp}.".encode() + payload
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, parts["v1"]):
raise ValueError("invalid signature")
```
{% endtab %}
{% endtabs %}
## Common pitfalls
<details>
<summary>"My signature verification fails on every request"</summary>
Almost always one of:
* **Body is parsed before verification.** Frameworks like Express, Flask, Rails default to parsing JSON. You need the raw bytes that arrived on the wire.
* **Wrong signing secret.** Each endpoint has its own. Test-mode and live-mode endpoints have different secrets too.
* **Wrong endpoint.** The dashboard shows what URL Evolve is sending to.
</details>
<details>
<summary>"Signature works locally but fails in production"</summary>
Check whether something in front of your server (nginx, a load balancer, a CDN) is rewriting the body — even adding a trailing newline breaks HMAC verification.
</details>
<details>
<summary>"I'm getting 'timestamp too old' errors"</summary>
Either your server's clock is way off NTP, or you're processing events that were buffered for more than 5 minutes (e.g. a backlog after an outage). Bump the tolerance for backlog processing, or reject and let Evolve retry — Evolve's retry will have a fresh timestamp.
</details>
## Rolling the signing secret
If you suspect the signing secret has leaked, roll it from **Developers → Webhooks → [endpoint] → Roll secret**. The new secret is active immediately; the old one keeps working for 24 hours so you have time to deploy.
## Related
* [Authentication](../getting-started/authentication.md) — for the outbound side (your requests to Evolve).
* [Retries and replay](retries-and-replay.md) — what happens when verification fails on your side.
* [Event catalog](event-catalog.md) — every event type Evolve sends.
references/example-site/guides/help-center/.gitbook/vars.yaml
api_live: https://api.evolve.com
api_test: https://api.test.evolve.com
dashboard_live: https://dashboard.evolve.com
dashboard_test: https://dashboard.test.evolve.com
support_email: support@evolve.com
status_page: https://status.evolve.com
community_forum: https://community.evolve.com
youtube_channel: https://youtube.com/@evolvepay
references/example-site/guides/help-center/account-and-security.md
---
icon: user-shield
description: Team access, SSO, audit logs, key management, and account-level security questions.
---
# Account and security
## How do I invite a team member?
Open **Settings → Team → Invite member** in the dashboard. Enter their email and pick a role (Admin, Developer, Operator, Finance, or Viewer). They'll receive an email and join your account once they accept.
You can change a member's role any time, and the change takes effect immediately.
## What roles are available, and what can each one do?
Five roles, each progressively more restricted:
| Role | Can do |
| --- | --- |
| **Admin** | Everything, including managing other members and billing. |
| **Developer** | API keys, webhook endpoints, integration testing. No billing. |
| **Operator** | Issue refunds, respond to disputes, manage payouts. No billing or API keys. |
| **Finance** | Read-only on payments, full access to settlements and reports. |
| **Viewer** | Read-only across the dashboard. |
You can also create custom roles with specific permissions in **Settings → Team → Custom roles** on Enterprise.
## How do I set up SSO?
SSO is available on Growth and Enterprise. In **Settings → Security → SSO**, pick your provider (Okta, Google Workspace, Microsoft Entra ID, or generic SAML/OIDC) and follow the per-provider setup. Most teams have it working in 30 minutes.
After SSO is enabled, password-based logins are disabled for everyone except break-glass admins (you can configure this).
For SCIM (auto-provisioning team members from your IdP), see the [community forum](https://gitbookio.github.io/evolve-demo/connections/community/).
## How do I rotate an API key?
In **Developers → API keys**, click **Roll** next to the key. The new key is active immediately; the old one keeps working for 24 hours so you can deploy without downtime. Revoke the old one once you've confirmed the new one is in place.
For incident response (suspected leakage), revoke immediately rather than rolling — anyone with the old key loses access at once. See the [webhook deep-dive on YouTube](https://gitbookio.github.io/evolve-demo/connections/youtube/webhooks-deep-dive.html) for the full incident-response flow.
## What does Evolve do if a key leaks publicly?
Evolve runs leaked-key detection across public GitHub commits, public Gitlab, common paste sites, and a few cloud-provider misconfigurations. If we detect your key in any of those, we auto-revoke it and email you within minutes — usually before any unauthorized requests can land.
Auto-revocation can't be turned off. If you'd rather receive a warning without immediate revocation, contact support.
## How do I export my audit log?
The audit log is exportable from **Compliance → Audit log → Export**. Choose a date range, columns, and format (CSV or JSON). Exports under 10,000 rows generate immediately; larger exports email you when ready.
For continuous export to your SIEM (Splunk, Datadog, etc.), the [reporting webhooks doc](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/event-catalog) covers the per-event push pattern.
## Can I require 2FA for all team members?
Yes. **Settings → Security → Two-factor authentication → Require for all members**. Existing members get a 7-day grace period to enroll; new members must enroll on first login.
If you also have SSO enabled, your IdP's MFA controls take precedence — Evolve's 2FA only applies to non-SSO admins (typically break-glass accounts).
## What's Evolve's security posture?
We're SOC 2 Type II audited annually, ISO 27001 certified, and PCI-DSS Level 1 compliant. Reports are available under NDA from your account team.
Encryption at rest uses per-tenant keys (KMS-managed); Enterprise customers can BYOK. Data residency in EU and US regions is configurable in **Settings → Security → Data residency**.
## What happens if I close my account?
Closing an account is a multi-step process to make sure there's no money left to move. Contact your account team to start it; they walk you through:
1. Disabling new charges.
2. Settling any outstanding balance.
3. Issuing any pending payouts.
4. Final settlement file.
5. Closing the account.
Account data is retained per your retention policy (default 7 years for compliance records, 30 days for verification PII). Closed accounts cannot be reopened with the same name; sign up fresh if you change your mind.
## Where can I find more answers?
* [Community forum](https://gitbookio.github.io/evolve-demo/connections/community/)
* [YouTube: Webhooks deep-dive](https://gitbookio.github.io/evolve-demo/connections/youtube/webhooks-deep-dive.html)
* [Developer docs: Authentication](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/authentication)
* [Identity compliance: Audit logs](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/compliance/audit-logs)
references/example-site/guides/help-center/billing-and-plans.md
---
icon: file-invoice-dollar
description: Plan tiers, invoices, volume pricing, and how to switch between plans.
---
# Billing and plans
## What's the difference between Starter, Growth, and Enterprise?
Three tiers, gating both feature access and pricing:
| | Starter | Growth | Enterprise |
| --- | --- | --- | --- |
| Per-transaction (cards) | 2.9% + $0.30 | 2.7% + $0.30 | Custom |
| Volume cap | $50K/mo | $1M/mo | Uncapped |
| Payout schedule | T+3 | T+2 | T+1 (same-day available) |
| Connect (marketplaces) | — | Up to 100 sellers | Unlimited |
| Identity verification | Basic only | Cards + ACH | All flows |
| SSO | — | ✅ | ✅ |
| Custom contracts | — | — | ✅ |
For the full breakdown, see [Payments → Fees and pricing](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/fees-and-pricing).
## How do I see my current plan?
**Settings → Billing → Current plan.** You'll see your tier, your current month's volume, your effective rates (which may differ from the published rates if you're on a custom contract), and your next invoice estimate.
## How do I change plans?
**Settings → Billing → Change plan**. Upgrades take effect immediately; downgrades take effect at the end of the current billing month (so you don't lose access mid-month).
To move to Enterprise, the **Contact sales** button starts the conversation. Most Enterprise contracts close in 2–4 weeks.
## When do I get charged?
Per-transaction fees are netted from your settlement file each day — not invoiced separately. Your bank deposit equals gross volume minus fees minus refunds.
Add-ons (custom reports beyond 50, dispute alerts, on-demand payouts, etc.) are invoiced monthly. Invoices arrive on the 1st of each month for the prior month's add-on usage.
## Where do I find my invoices?
**Settings → Billing → Invoices**. Each month's invoice is downloadable as PDF. For finance teams who want them auto-emailed, configure recipients under **Settings → Billing → Invoice delivery**.
## Can I get volume discounts?
Yes, on Enterprise. Volume discount tiers kick in starting at $10M annual processing volume; the specifics depend on your card mix, region, and risk profile. Contact your account team.
For Growth customers approaching $1M/month consistently, Enterprise is usually the better economics — the conversation is worth having even if you're not at the cap yet.
## Are there setup fees or monthly minimums?
No setup fees. No monthly minimums on Starter or Growth. Enterprise contracts may include a minimum monthly commitment; that's negotiated as part of the contract.
## What if I exceed my plan's volume cap?
On Starter, you'll get email warnings at 80% and 95% of cap. At 100%, new charges fail with `volume_cap_exceeded` until you upgrade. You can upgrade mid-month and the new cap applies immediately.
On Growth, the same warnings fire at 80% and 95% of $1M; there's no hard cap, but rates above the cap scale to a flat 3.4% + $0.30 to encourage Enterprise migration.
## What happens to my refund processing fees?
When you take a payment, Evolve charges you the processing fee. When you refund it, the fee stays — you've paid the network and that fee isn't recoverable. The refund goes through at no additional cost from Evolve, but the original processing fee on the payment becomes a sunk cost.
This isn't unique to Evolve — every major processor works this way. See the [community thread on decline-code triage](https://gitbookio.github.io/evolve-demo/connections/community/decline-codes-vs-card-decline-codes.html) for how teams typically book this.
## Why was my account flagged for risk review?
Risk reviews can fire for a few reasons: rapid volume growth, a change in card-mix or geographic mix, a rising dispute rate, or unusual chargeback patterns. The dashboard shows the review reason and what's needed.
Most risk reviews resolve within 2 business days with no action from you. If we need information, you'll get an email and the dashboard will show a banner. Contact your account team if it's been longer than 5 business days.
## Where can I find more answers?
* [Community forum](https://gitbookio.github.io/evolve-demo/connections/community/)
* [YouTube: Same-day payouts tradeoffs](https://gitbookio.github.io/evolve-demo/connections/blog/same-day-payouts-tradeoffs.html)
* [Payments → Fees and pricing](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/fees-and-pricing)
references/example-site/guides/help-center/connect-questions.md
---
icon: circles-overlap
description: Connected accounts, splits, payouts, marketplace patterns — the most-asked Connect questions.
---
# Connect questions
## Do I need Connect?
Yes if you take payments **on behalf of others** — marketplaces, B2B platforms paying out to vendors, SaaS apps that route money to customers. No if you only take payments **for yourself** (typical e-commerce, SaaS billing, donations).
If you're not sure: ask "does the money belong to me, or to someone using my product?" If the latter, that's Connect.
## What plan do I need for Connect?
Growth or Enterprise. Starter doesn't include Connect.
| | Growth | Enterprise |
| --- | --- | --- |
| Connected accounts | Up to 100 | Unlimited |
| Hosted onboarding | ✅ | ✅ |
| Embedded checkout | — | ✅ |
| Custom (white-label) onboarding | — | ✅ |
| Per-seller dispute routing | ✅ | ✅ |
For the full per-tier breakdown, see [Connect → Plan availability](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/#plan-availability).
## How do I onboard a seller?
Two integration shapes:
* **Hosted** — Evolve generates an onboarding URL, you email it to the seller. Most platforms launch with this. The walkthrough is in the [Onboard your first sellers tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/marketplace/onboard-sellers).
* **Custom** — Enterprise-only. You build your own forms; data is submitted programmatically to Evolve. See [Build a custom Connect onboarding flow](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/marketplace/custom-onboarding).
Most teams should use hosted unless brand standards or specific UX requirements demand custom. Hosted updates automatically as KYC requirements change; custom requires you to keep up.
## Why is my seller's account `restricted`?
Risk team has flagged something. The seller's record in **Connect → Connected accounts → [account] → Status** shows the specific reason. Common ones:
* High dispute rate (above 1%)
* Sudden volume spike that triggers fraud screening
* Sanctions match found during ongoing monitoring
* Manual review pending after a flagged transaction
Restricted accounts can't take new charges; existing balances pay out normally. Contact your account team to discuss next steps for the specific seller.
## How do I take my platform fee on each payment?
Set `application_fee_amount` when creating the Checkout session:
```http
POST /v2/checkout_sessions
{
"amount": 10000,
"currency": "usd",
"connected_account": "acct_3KsM12pL9q",
"application_fee_amount": 200
}
```
That's $100 to the buyer, $98 transferred to the seller, $2 to your platform. The [split-payments tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/marketplace/split-payments) covers conditional fee logic (per-seller-tier, per-product-category, etc.).
## How are processing fees split between platform and seller?
By default, the card processing fee comes off the platform's application fee. Pass `application_fee_includes_processing: false` to make the seller absorb it instead.
Most marketplaces with thin take-rates pass through to sellers. Most marketplaces competing on seller experience absorb. Pick once at the platform level and keep it consistent.
## Can a seller have a custom payout schedule?
Yes. The platform sets the default in **Connect → Settings → Default payout schedule**. Sellers can override their own (within the schedules you allow) from their seller portal.
For on-demand payouts (seller taps "Pay me now" for instant cash), enable in **Connect → Settings → Instant payouts**. The 1% fee can be paid by seller, platform, or split. Available on Enterprise. See [payout-schedules tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/marketplace/payout-schedules).
## What happens to disputes on a Connect platform?
The **platform is the merchant of record** — disputes are filed against the platform's merchant ID. Three policies for who absorbs the disputed amount + $15 fee:
* **Pass to seller** — most common.
* **Platform absorbs** — for premium-tier sellers as a perk.
* **Split** — platform takes the fee, seller takes the disputed amount.
Set the default in **Connect → Settings → Dispute policy**, override per-seller. For evidence collection, most platforms delegate to the seller — see [disputes-at-scale tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/marketplace/disputes-at-scale).
## Why is a seller's payout `failed`?
Bank account became invalid — closed, frozen, name mismatch, or wrong details. The dashboard shows the bank's reason code. The amount returns to the seller's balance; they update the bank account in their portal and the next scheduled payout includes the failed amount.
Three failed payouts in a row auto-pause the seller until you intervene. See the [community thread on stuck Connect payouts](https://gitbookio.github.io/evolve-demo/connections/community/connect-payout-stuck.html) for the typical playbook.
## Can I have a seller in a country my platform doesn't operate in?
Sometimes — depends on the country pair. Some platform/seller country combinations have regulatory or banking restrictions. Talk to your account team before promising a new seller country to your operators.
For platforms operating in multiple countries themselves, you can configure per-region defaults (different fee structure, different payout schedule) in **Connect → Settings → Per-region**.
## What's the difference between direct charges and destination charges?
* **Direct charge** — the seller is the merchant of record. Set `Evolve-Account: acct_*` header on the charge. The seller's statement descriptor shows on the buyer's bank statement.
* **Destination charge** — the platform is the merchant of record. The platform's descriptor shows. A separate transfer moves the funds to the seller's connected account.
Most Connect platforms use destination charges — buyers see a consistent platform brand, and the platform has clearer dispute responsibility. Pick once at the platform level in **Connect → Settings → Charge type**.
## Where can I find more answers?
* [Community: Stuck Connect payouts](https://gitbookio.github.io/evolve-demo/connections/community/connect-payout-stuck.html)
* [YouTube: Chargebacks at scale](https://gitbookio.github.io/evolve-demo/connections/youtube/chargebacks-at-scale.html)
* [Connect product space](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/)
* [Tutorials: Run a marketplace with Connect](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/#run-a-marketplace-with-connect)
references/example-site/guides/help-center/getting-started.md
---
icon: flag
description: First setup, going live, integration help, and the steps every new account walks through.
---
# Getting started
## I just signed up. What do I do first?
Three steps in order:
1. **Verify your business** — answer the prompts in **Settings → Onboarding** to verify your business identity and link a bank account. Until this is done, you're stuck in test mode.
2. **Run a test charge** — follow the [Quickstart](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/quickstart/accept-your-first-payment). Five minutes, no integration required.
3. **Get your live keys** — once business verification passes, **Developers → API keys** has live `sk_live_` and `pk_live_` keys ready.
After that, the [Tutorials](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/) cover the most common integration patterns.
## How long does business verification take?
For US-based businesses with standard structures (LLC, S-corp, sole proprietor), most verifications complete in **1–2 business days**. For more complex structures (multi-layer ownership, international entities, regulated verticals), it can take up to 5 business days.
You can use test mode immediately while verification is pending. Live charges are blocked until verification completes.
## What documents will I need?
Standard business verification needs:
* Legal business name and registration country.
* EIN (US) or local equivalent.
* Business address.
* Beneficial owners (for any entity beyond a sole proprietor).
* A bank account for payouts (Plaid-instant or routing/account number).
For regulated verticals (financial services, gambling, age-restricted goods, money transmission), additional documentation is required. The dashboard prompts for whatever's needed; if you're unsure, talk to your account team before submitting.
## Can I use Evolve in countries that aren't in the dropdown?
The country list in the dashboard reflects where Evolve is currently licensed. If your business is in a country not listed, contact sales — coverage expands regularly and you may be able to onboard via a special-process route.
For sellers in unsupported countries on a Connect platform, the path is usually different — Evolve can onboard them via the platform's account if certain regulatory conditions are met.
## How do I get my first API key?
**Developers → API keys** in the dashboard. You'll see two pairs by default — `sk_test_*` / `pk_test_*` for test mode and (after business verification) `sk_live_*` / `pk_live_*` for live mode.
Copy the secret keys to a password manager or environment-variable store. Don't commit them to source control.
For the full setup walkthrough, see the [Developers Quickstart](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/quickstart).
## What's the fastest way to take my first payment?
The [Accept your first payment](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/quickstart/accept-your-first-payment) walkthrough takes about five minutes via the dashboard, with no code required. Create a payment link, share it, complete the test checkout — done.
For an integration-first start, the [Tutorials → Accept a one-time payment](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/accept-one-time-payment) covers building Checkout into your own site.
## How do I switch from test mode to live mode?
There's no migration step — test and live are fully separate. To go live:
1. Confirm business verification has completed.
2. Generate a live key in **Developers → API keys**.
3. Replace `sk_test_*` with `sk_live_*` in your environment configuration.
4. Update webhook endpoints to point at your production URL with the live signing secret.
Test-mode data doesn't carry over. Most teams run a single $1 live charge as a smoke test before pointing real customers at the live key. See the [Test mode and live mode page](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/quickstart/test-and-live-mode) for the full cutover checklist.
## Which SDK should I use?
Evolve maintains four official SDKs: Node, Python, Go, Ruby. They're feature-equivalent — pick whatever your team uses. For mobile apps, see the iOS / Android / React Native libraries instead, which are built around card collection.
The full list is on the [Developers SDKs page](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/sdks).
## I'm migrating from another provider. Where do I start?
For Stripe specifically, the [Migrate from Stripe tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/migrate-from-stripe) covers the field mapping, the parallel-run pattern, and the cutover checklist. For other providers, the same patterns apply — most concepts (charges, refunds, customers, subscriptions) map cleanly.
For business-side migration of customer data and saved cards, contact your account team. Several import tools are available depending on the source provider.
## Can someone help me design my integration?
Yes — Enterprise customers get a solutions engineer assigned at signup. Growth customers can request integration support via **Settings → Get help → Integration support**; we'll respond within 1 business day.
For most well-trodden integration patterns, the [Tutorials](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/) cover the build end to end, including common pitfalls.
## Where can I find more answers?
* [Community forum](https://gitbookio.github.io/evolve-demo/connections/community/)
* [YouTube: Set up Connect in 10 minutes](https://gitbookio.github.io/evolve-demo/connections/youtube/set-up-connect-in-10-minutes.html)
* [Tutorials](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/) — end-to-end builds for common workflows.
* [Developers Quickstart](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/quickstart) — five-minute API integration.
references/example-site/guides/help-center/identity-questions.md
---
icon: id-card
description: Verification flows, decisions, retention, and the most-asked Identity product questions.
---
# Identity questions
## Which verification flow do I need?
Three flows, picked by what you're verifying:
| Verifying... | Use this flow |
| --- | --- |
| An individual customer | Identity verification (document + selfie) |
| A bank account belongs to the customer | Bank account verification (Plaid or micro-deposits) |
| A business and its owners | Business verification (KYB) |
Most consumer products need only identity verification. ACH-accepting products add bank verification. Marketplaces and B2B platforms add KYB. The [decision tree](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows#a-decision-tree) shows it visually.
## How long does verification take?
| Flow | Time |
| --- | --- |
| Identity (document + selfie) | 60–90 seconds for the customer; result in seconds |
| Bank verification (Plaid) | 30–60 seconds for the customer; result in seconds |
| Bank verification (micro-deposits) | 1–2 business days |
| Business verification (KYB, simple LLC) | 2 business days end-to-end |
| Business verification (complex ownership) | Up to 10 business days |
Manual review (when an automated check is inconclusive) typically resolves within an hour during business hours.
## Why did a verification fail?
The reason code on the session timeline tells you. The most common ones:
| Code | Cause | What customer can do |
| --- | --- | --- |
| `document_expired` | ID is past expiry | Provide a current document |
| `document_tampered` | Pixel analysis detected manipulation | Retry with different document; flagged as fraud |
| `document_unrecognized` | Couldn't match a template; usually a partial capture | Recapture |
| `selfie_mismatch` | Face doesn't match the ID photo | Retry; if persistent, manual review |
| `liveness_failed` | Spoof attempt detected | No retry — fraud |
| `data_mismatch` | Typed data doesn't match document | Correct typed data |
For the full code reference, see [Document review](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/identity-verification/document-review).
## What countries can I verify in?
Identity verification (document + selfie) works in **195 countries** — every country except those on the OFAC blocklist. Specific document type support varies by country; the dashboard's country picker shows what's supported.
Bank verification (Plaid) is broadest in the US, with Canada, UK, France, Spain, Netherlands, and Ireland in coverage. For other countries, micro-deposits or country-specific open-banking flows are alternatives — talk to your account team for non-listed countries.
Business verification (KYB) covers ~50 countries through national-register integrations. Outside that list, KYB falls back to manual document review which takes longer.
## How do I trigger re-verification?
Three patterns most teams use:
1. **On a chargeback** — fraud signal warrants a fresh check.
2. **On a transaction over a threshold** — high-value implies higher trust requirements.
3. **On a profile change** — address change to a different country, name change, etc.
The [re-verification tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/verification/re-verification-trigger) covers the full implementation. For scheduled re-verification (every N months for regulated regimes), use the dashboard's scheduled re-verification feature instead of building it yourself.
## How long is verification data retained?
Two retention windows:
* **Raw images** (document fronts, backs, selfies) — 30 days by default. Configurable from 1 day to 7 years in **Settings → Identity → Retention**.
* **Extracted data and decisions** (name, DOB, document number, decision reason) — 7 years by default. Required by most regulatory regimes.
For customer deletion requests under GDPR/CCPA, see [Data retention → Customer deletion requests](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/compliance/data-retention#customer-deletion-requests).
## Can I see the verified data on the customer record?
Yes, from **Identity → Sessions → [session]**. Verified PII (extracted name, DOB, address) shows in the session detail page. Be careful exposing this to other parts of your team — leakage of verified PII to the wrong dashboard view is the most-cited compliance issue we see.
The [audit log](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/compliance/audit-logs) records every PII view, with `pii.accessed` events tied to the team member.
## What is watchlist screening, and do I need it?
Watchlist screening checks an identity against sanctions lists (OFAC, UN, EU, UK HMT), PEP lists, adverse media, and your own internal blocklists.
You probably need it if:
* You operate in a regulated vertical (financial services, money transmission, gambling, crypto).
* You're a marketplace and your sellers are subject to payment-aggregator obligations.
* Your bank or auditor has told you that you need it.
You probably don't need it for typical e-commerce or community products.
Watchlist screening is Enterprise-only. See [Watchlist screening](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/identity-verification/watchlist-screening).
## How does ongoing monitoring work?
For verified businesses (KYB), Evolve re-screens against the latest sanctions lists weekly. If a previously-clear entity matches a newly-added entry, you get a `screening.match_added` webhook and a dashboard alert.
Most teams suspend the entity pending a manual review. The cost is included with the original KYB at no extra fee.
## What's the difference between Plaid instant and micro-deposits?
| | Plaid instant | Micro-deposits |
| --- | --- | --- |
| Speed | 30–60 seconds | 1–2 business days |
| Coverage | ~12,000 US banks | Any US bank |
| Cost | $2.50/verification | $0.80/verification |
| Customer effort | Sign in to bank | Wait for deposits, type amounts |
| Risk signals | Yes (balance, NSF history) | No |
Most teams use Plaid first with micro-deposits as fallback — covered automatically when you set `method: "auto"`. Walked through in the [Plaid bank verification tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/verification/plaid-bank-verification).
## Where can I find more answers?
* [Community: KYB beneficial-ownership](https://gitbookio.github.io/evolve-demo/connections/community/kyb-beneficial-ownership-edge-cases.html)
* [YouTube: KYB end-to-end](https://gitbookio.github.io/evolve-demo/connections/youtube/kyb-end-to-end.html)
* [Identity product space](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/)
* [Tutorials: Verify customers and businesses](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/#verify-customers-and-businesses)
references/example-site/guides/help-center/payments-questions.md
---
icon: credit-card
description: Charges, refunds, payouts, settlement — the most-asked Payments product questions.
---
# Payments questions
## Why was my customer's card declined?
The decline reason is on the charge timeline as `decline_code`. The most common ones:
| Code | What it means | What to do |
| --- | --- | --- |
| `insufficient_funds` | Not enough money on the card | Ask the customer to use another card |
| `expired_card` | Card past its expiry date | Collect updated details |
| `incorrect_cvc` / `incorrect_zip` | Wrong CVC or billing ZIP | Re-prompt for the failed field |
| `do_not_honor` | Generic decline; bank doesn't say why | Sometimes worth retrying after 24h |
| `lost_card` / `stolen_card` | Card flagged as compromised | Don't retry — fraud signal |
For a deeper dive, see the [community thread on decline-code triage](https://gitbookio.github.io/evolve-demo/connections/community/decline-codes-vs-card-decline-codes.html).
## When do my funds become available for payout?
A captured payment becomes available the moment it's captured. The available balance pays out on your plan's schedule:
| Plan | Schedule |
| --- | --- |
| Starter | T+3 business days |
| Growth | T+2 business days |
| Enterprise | T+1 (same-day available) |
The full mechanic — including reserves, holds, and multi-currency — is on the [Money movement and settlement](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/money-movement) page.
## Why is my payout pending?
Three common reasons:
1. **Risk hold** — Evolve flagged a payment for review. The hold lasts up to 14 days. The payment shows `held_for_review: true` on the timeline.
2. **Account reserve** — your account has a rolling reserve (typically 5% over 90 days for new accounts). The reserved amount stays pending until it ages out.
3. **Bank account issue** — your linked bank account became invalid. The dashboard shows a banner; update the account and the payout retries.
For risk holds specifically, contact support if it's been over 14 days.
## How do I issue a refund?
From the charge page in the dashboard, click **Refund**. You can refund the full amount or a partial amount; multiple partial refunds are fine up to the original total.
Via API, `POST /v2/refunds` with the charge ID. See the [Refunds tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/save-cards) for code samples.
## Why isn't my webhook firing?
Five things to check, in order:
1. **Endpoint subscribed to the right event?** Check **Developers → Webhooks → [endpoint] → Events**.
2. **Endpoint URL correct and reachable?** Use the dashboard's "Test endpoint" button.
3. **Endpoint returning 2xx?** Anything else triggers retries.
4. **Signature verification passing?** Most-cited cause: body parsed before verification.
5. **Live vs test mode mismatch?** Test-mode events only fire to test-mode endpoints, and vice versa.
The [Verifying signatures page](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/verifying-signatures) has the full debugging checklist. Watch the [YouTube webhook-debugging walkthrough](https://gitbookio.github.io/evolve-demo/connections/youtube/webhooks-deep-dive.html) for the live-troubleshooting flow.
## What's the difference between authorize and capture?
Authorize reserves the money on the customer's card without taking it. Capture finalizes the charge and the funds start moving toward your account.
By default, charges authorize and capture in one step. Pass `capture: false` to split them — useful for pre-orders, hospitality, marketplaces. You then have up to 7 days (30 on v3) to capture before the authorization expires.
Full mechanics on the [Payment lifecycle page](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/payment-lifecycle).
## How do I handle disputes?
Disputes show up in **Reconciliation → Disputes**. For each dispute:
1. Read the reason code — it tells you what evidence to gather.
2. Decide to fight or accept. For low-value disputes (under $50), accepting is often cheaper than the staff time to fight.
3. If fighting, submit evidence within 20 calendar days. The form pre-populates the most-relevant fields.
For systematic dispute reduction, the [chargeback-prevention tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/chargeback-prevention) is the highest-ROI thing you can do.
## Why is my approval rate low?
Three common causes:
* **Statement descriptor** — customers don't recognize charges and call their bank. Fix the descriptor in **Settings → Billing**.
* **Single acquirer** — you're missing the lift from Smart routing (Growth+). Check **Settings → Routing**.
* **Card mix** — international cards typically have lower approval rates. The Routing report breaks it down per BIN country.
For Growth and Enterprise, [Smart routing](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/accept-payments/smart-routing) typically lifts approval rates 1–3% with no code changes.
## Can I do partial captures?
Yes. After authorizing, capture an amount less than or equal to the original authorization. The remaining amount is released back to the customer.
```http
POST /v2/charges/{id}/capture
{
"amount": 8000
}
```
Useful for hospitality (you authorize $200 at check-in, capture $147 at check-out) and marketplaces (you authorize the buyer's full amount, capture once you know what each seller actually delivered).
## What payment methods can I accept?
Cards (Visa, Mastercard, Amex, Discover, JCB, UnionPay, Diners) on every plan. ACH debit on Growth and Enterprise. Wire, SEPA, BACS Direct Debit, and 100+ international rails on Enterprise.
For the per-plan breakdown, see [Payment methods](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/payment-methods).
## How do I configure 3-D Secure?
In **Settings → Risk → 3-D Secure**, three presets:
* **Required only** — Evolve handles required cases (EU SCA), nothing else.
* **By rule** — your custom rules (e.g. amount > $500) on top of required cases.
* **Always** — every charge goes through 3DS.
For most teams, **By rule** with a high-value threshold is the right default. The [3-D Secure tutorial](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/3d-secure) walks through the setup.
## Where can I find more answers?
* [Community: Stripe migration gotchas](https://gitbookio.github.io/evolve-demo/connections/community/migrating-stripe-customers.html)
* [YouTube: Smart routing explained](https://gitbookio.github.io/evolve-demo/connections/youtube/smart-routing-explained.html)
* [Payments product space](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/)
* [Tutorials: Build common payment flows](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/#build-common-payment-flows)
references/example-site/guides/help-center/README.md
---
icon: life-ring
description: Answers to common questions, with the Assistant as your fastest path to a specific one.
cover: .gitbook/assets/help-cover.png
coverY: 0
layout:
width: wide
tableOfContents:
visible: false
---
# Help Center
{% columns %}
{% column width="50%" %}
The Help Center is a curated set of focused answers to the questions our customers ask most. The Assistant pulls answers from this site, our [community forum](https://gitbookio.github.io/evolve-demo/connections/community/), [YouTube channel](https://gitbookio.github.io/evolve-demo/connections/youtube/), and [engineering blog](https://gitbookio.github.io/evolve-demo/connections/blog/) — so for most questions, asking the Assistant is faster than browsing.
<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">Ask the Evolve docs</button>
<button type="button" class="button secondary" data-action="ask" data-query="How do I rotate an API key?" data-icon="key">API keys</button> <button type="button" class="button secondary" data-action="ask" data-query="How do I change my pricing plan?" data-icon="file-invoice-dollar">Plan changes</button> <button type="button" class="button secondary" data-action="ask" data-query="Why is my webhook not firing?" data-icon="bolt">Webhooks</button> <button type="button" class="button secondary" data-action="ask" data-query="Why was my customer's card declined?" data-icon="circle-xmark">Card declines</button>
{% endcolumn %}
{% column width="50%" %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This space demonstrates **Connections**: the Assistant uses external sources (the [community forum](https://gitbookio.github.io/evolve-demo/connections/community/), [YouTube channel](https://gitbookio.github.io/evolve-demo/connections/youtube/), and [engineering blog](https://gitbookio.github.io/evolve-demo/connections/blog/)) alongside the docs to answer questions. Each FAQ page is structured for **AI retrieval** — H2-style question headings, focused answers, no preamble — so the Assistant can return a clean excerpt rather than a wall of text.
Try a search that pulls across all sources:
<button type="button" class="button secondary" data-action="search" data-query="Stripe migration gotchas" data-icon="magnifying-glass">Search "Stripe migration gotchas"</button>
{% endhint %}
{% endcolumn %}
{% endcolumns %}
## Browse by topic
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-user-shield" style="color:$primary;">:user-shield:</i></h3></td><td><strong>Account and security</strong></td><td>Team access, SSO, audit logs, key management.</td><td><a href="account-and-security.md">account-and-security.md</a></td></tr><tr><td><h3><i class="fa-file-invoice-dollar" style="color:$primary;">:file-invoice-dollar:</i></h3></td><td><strong>Billing and plans</strong></td><td>Plan tiers, invoices, volume pricing, plan changes.</td><td><a href="billing-and-plans.md">billing-and-plans.md</a></td></tr><tr><td><h3><i class="fa-flag" style="color:$primary;">:flag:</i></h3></td><td><strong>Getting started</strong></td><td>First setup, going live, integration help.</td><td><a href="getting-started.md">getting-started.md</a></td></tr><tr><td><h3><i class="fa-credit-card" style="color:$primary;">:credit-card:</i></h3></td><td><strong>Payments questions</strong></td><td>Charges, refunds, payouts, settlement.</td><td><a href="payments-questions.md">payments-questions.md</a></td></tr><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><strong>Identity questions</strong></td><td>Verification flows, decisions, retention.</td><td><a href="identity-questions.md">identity-questions.md</a></td></tr><tr><td><h3><i class="fa-circles-overlap" style="color:$primary;">:circles-overlap:</i></h3></td><td><strong>Connect questions</strong></td><td>Connected accounts, splits, marketplace patterns.</td><td><a href="connect-questions.md">connect-questions.md</a></td></tr></tbody></table>
## Other places to look
{% columns %}
{% column width="33%" %}
### <i class="fa-comments" style="color:$primary;">:comments:</i> Community forum
Real-time discussion with other Evolve customers and our team. Most product questions have a thread.
<p><a href="https://gitbookio.github.io/evolve-demo/connections/community/" class="button secondary">Visit the forum</a></p>
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-youtube" style="color:$primary;">:youtube:</i> YouTube channel
Walkthroughs, deep-dives, monthly product updates. Subscribe for new releases.
<p><a href="https://gitbookio.github.io/evolve-demo/connections/youtube/" class="button secondary">Watch on YouTube</a></p>
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-circle-question" style="color:$primary;">:circle-question:</i> Talk to support
For account-specific issues or production incidents, the support team is your fastest path.
<p><a href="https://gitbook.com" class="button primary">Open a ticket</a></p>
{% endcolumn %}
{% endcolumns %}
## Status
If something across Evolve isn't working, check [status.evolve.com](https://gitbook.com) before opening a ticket. We post updates there for any platform-wide issue within a few minutes of detection.
references/example-site/guides/help-center/SUMMARY.md
# Table of contents
* [Help Center](README.md)
* [Account and security](account-and-security.md)
* [Billing and plans](billing-and-plans.md)
* [Getting started](getting-started.md)
* [Payments questions](payments-questions.md)
* [Identity questions](identity-questions.md)
* [Connect questions](connect-questions.md)
references/example-site/guides/integrations/.gitbook/vars.yaml
api_live: https://api.evolve.com
dashboard_live: https://dashboard.evolve.com
support_email: support@evolve.com
references/example-site/guides/integrations/netsuite/custom-field-mapping.md
---
icon: arrows-up-down-left-right
description: Map Evolve metadata to NetSuite custom segments — departments, classes, locations, and custom-defined.
---
# Custom field mapping
NetSuite's custom segments are how multi-divisional, multi-region, and project-tracked accounting works. Evolve's bundle exposes a mapping screen where you bind Evolve metadata keys to NetSuite segments, so each journal entry posts to the right slice automatically.
## NetSuite segments supported
| NetSuite segment | What it represents | Common Evolve metadata source |
| --- | --- | --- |
| **Department** | Organizational unit (Sales, Engineering) | `metadata.department` |
| **Class** | Cross-cutting category (Subscriptions, Hardware) | `metadata.product_line` |
| **Location** | Physical site (Store 42, Warehouse East) | `metadata.location_id` or `metadata.store_id` |
| **Subsidiary** | Legal entity in a OneWorld account | Evolve account, or `metadata.subsidiary_id` |
| **Custom segments** | Anything you've defined (Project, Region, Cohort) | Per your mapping |
## Setting up a mapping
In NetSuite, the bundle's setup screen (under **Customization → SuiteApps → Evolve → Setup**) shows a row per custom segment. For each one:
1. Pick which segment values are available (NetSuite gives you a list).
2. Set the **default value** — what to post if the metadata key is missing.
3. Set the **metadata key** — which Evolve `metadata.X` field the bundle reads.
4. (Optional) Define a **mapping table** for translating Evolve values to NetSuite internal IDs.
Save. From the next sync onward, journal entries are tagged correctly.
## Mapping tables
When the Evolve metadata value doesn't match the NetSuite segment value verbatim, use a mapping table:
| Evolve metadata value | NetSuite segment internal ID |
| --- | --- |
| `subscriptions` | 12 (Class: Subscriptions) |
| `commerce` | 14 (Class: E-commerce) |
| `services` | 18 (Class: Professional Services) |
The bundle keeps the mapping table cached; updating it requires no resync — future entries just use the new mapping.
## Multi-subsidiary specifics
For Connect platforms with sellers in multiple legal entities, the typical setup:
* Tag each connected account with `metadata.subsidiary_id` during onboarding.
* Map `metadata.subsidiary_id` → NetSuite subsidiary in the bundle.
* Charges to that connected account post to the corresponding subsidiary's books.
For platforms charging multiple subsidiaries from a single Evolve account, the bundle handles inter-subsidiary eliminations on the FX side automatically — the elimination journal entries post nightly.
## Custom segment example
A common pattern: a SaaS company tracking revenue per product family AND per region:
```
Evolve metadata: { product_line: "subscriptions", region: "us" }
↓
NetSuite journal entry segments:
- Class: "Subscriptions" (from product_line mapping)
- Custom segment "Region": "United States" (from region mapping)
```
The reporting in NetSuite then slices on either or both — `Subscription revenue` × `US` = a single cell in your management report.
## Required vs optional segments
Some NetSuite installations require a value on every line item; others let lines have nulls. The bundle's setup tells you which:
* **Required segments** must have a default value or every journal entry needs a metadata value. The bundle errors out the day's sync if values are missing.
* **Optional segments** can be null; the bundle leaves them blank.
Most teams set sensible defaults (Region: "United States", Department: "Operations") to handle the cases where metadata is missing.
## Multi-currency and FX
For multi-currency Evolve accounts posting to multi-currency NetSuite, the bundle handles:
* Per-currency journal entries (one entry per currency per day).
* FX gain/loss postings on conversion (using NetSuite's exchange rate table).
* Currency-revaluation entries at month-end (configurable per subsidiary).
For complex setups (e.g., booking-currency vs reporting-currency mismatches), talk to your account team — there are several options depending on how your NetSuite is configured.
## Validating a mapping change
After changing a mapping:
1. Click **Sync test entry** in the bundle's setup screen. Posts a single test entry tagged with the new mapping.
2. Verify in NetSuite that the segments are correct.
3. If correct, click **Apply** to use the new mapping for future syncs.
The test entry is reversed automatically after 24 hours so it doesn't pollute your books.
## Last reviewed
Last reviewed in early 2026. Mapping options expand as NetSuite ships new custom-segment features. Suggest updates via [the docs repo](https://github.com/GitbookIO/evolve-demo).
## Related
* [NetSuite overview](README.md) — install and high-level setup.
* [QuickBooks reconciliation mapping](../quickbooks/reconciliation-mapping.md) — equivalent for QB customers.
* [Settlement files](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/settlement-files) — the source data.
references/example-site/guides/integrations/netsuite/README.md
---
icon: building
description: Multi-subsidiary journal entries, custom segment mapping, multi-currency reconciliation.
---
# NetSuite
The Evolve NetSuite integration is the heavier-duty cousin of [QuickBooks](../quickbooks/README.md) — designed for multi-subsidiary, multi-currency, NetSuite-customized accounting setups. It runs as a SuiteApp installed in your NetSuite environment, with daily journal entries posted from Evolve.
This is for Enterprise and large Growth customers. Setup takes a NetSuite administrator about 4 hours; ongoing maintenance is minimal.
## What it does
* **Daily journal entries** with full NetSuite custom-field mapping (departments, classes, locations, custom segments).
* **Multi-subsidiary support** — different Evolve accounts (or different `metadata.subsidiary_id` values) post to different NetSuite subsidiaries.
* **Multi-currency** with NetSuite's native FX gain/loss accounting.
* **Customer sync** with bidirectional updates between Evolve customers and NetSuite Entities.
* **SuiteScript hooks** — for teams with custom logic, the bundle exposes events your scripts can react to.
## Install the SuiteApp
{% stepper %}
{% step %}
### Get the bundle ID
In Evolve, **Settings → Integrations → NetSuite → Setup** shows your unique bundle ID. Copy it.
{% endstep %}
{% step %}
### Install in NetSuite
In NetSuite, **Customization → SuiteBundler → Search & Install Bundles**. Paste the bundle ID and install. The bundle adds:
* Custom record types: `Evolve Settlement`, `Evolve Charge`, `Evolve Refund`.
* Custom fields on Customer and Subsidiary.
* Saved searches for reconciliation.
* Daily scheduled scripts that pull from Evolve.
{% endstep %}
{% step %}
### Authenticate
Generate a TBA (Token-Based Authentication) token in NetSuite under **Setup → Users/Roles → Access Tokens**. Paste the consumer key, consumer secret, token, and token secret into the Evolve dashboard.
For SAML-SSO environments, use NetSuite's OAuth 2.0 flow instead — supported on Evolve Enterprise.
{% endstep %}
{% step %}
### Map subsidiaries
If you operate multiple NetSuite subsidiaries, map each Evolve account (or metadata-tagged subsidiary) to a NetSuite subsidiary in the bundle's setup screen. See [Custom field mapping](custom-field-mapping.md) for the multi-subsidiary patterns.
{% endstep %}
{% step %}
### Run the first sync
The bundle's daily scheduled script runs at 7am in your NetSuite account's timezone. To run an immediate first sync, click **Sync now** in the bundle's setup screen. It backfills the last 30 days of settlements by default.
Verify the journal entries in NetSuite. Most NetSuite admins involve their accountant to confirm the first three days of entries before letting it run unattended.
{% endstep %}
{% endstepper %}
## Multi-subsidiary mapping
Three patterns most teams use:
| Pattern | Setup |
| --- | --- |
| **One Evolve account per subsidiary** | Each subsidiary has its own Evolve account; the bundle maps account → subsidiary 1:1. Simplest. |
| **Single Evolve account, metadata-tagged** | One Evolve account; every charge has `metadata.subsidiary_id`. The bundle reads the metadata and posts to the right subsidiary. |
| **Routing rules** | One Evolve account; charges are routed to subsidiaries based on configurable rules (currency, country, product line). |
The choice depends on your NetSuite OneWorld setup. Most platforms with truly separate subsidiaries (different legal entities, different banks) use Pattern 1. Single-entity multi-divisional companies use Pattern 2 or 3.
## Custom segments
NetSuite's custom segments (departments, classes, locations, custom-defined) are exposed in the bundle's mapping screen. Tag each Evolve metadata key with the corresponding NetSuite segment, and journal entries get the right segment values automatically. Full mapping reference on [Custom field mapping](custom-field-mapping.md).
## Customer sync
Bidirectional. New Evolve customers can sync to NetSuite Entities; updated NetSuite Entities can sync back to Evolve customer records. Most teams enable one direction (typically Evolve → NetSuite) and disable the reverse to avoid sync loops.
For matching existing customers, the bundle uses email as the default key. For B2B with shared contact emails, you can switch to a tax-ID match instead.
## Last reviewed
Reviewed in early 2026. NetSuite bundle releases happen quarterly; release notes go through [change requests on the docs repo](https://github.com/GitbookIO/evolve-demo).
## Related
* [Custom field mapping](custom-field-mapping.md) — the deeper mapping options.
* [QuickBooks integration](../quickbooks/README.md) — for non-NetSuite teams.
* [Settlement files](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/settlement-files) — the source data.
references/example-site/guides/integrations/quickbooks/README.md
---
icon: calculator
description: Auto-create journal entries from each settlement. QuickBooks Online and Desktop.
---
# QuickBooks
The Evolve QuickBooks integration writes journal entries to your QuickBooks file each day, mapping settlement-file line items to your chart of accounts. Most teams set it up once at month-end-close-time and let it run unattended.
Two flavors:
* **QuickBooks Online** — direct OAuth integration, real-time sync.
* **QuickBooks Desktop** — IIF file export with daily download or SFTP delivery. Manual import into Desktop.
## What it does
* For each daily settlement file, generate a journal entry in QuickBooks with:
* Revenue (gross from charges) → your revenue account.
* Processing fees → your fees account.
* Refunds → reduce revenue.
* Disputes (lost) → bad-debt expense.
* Net to bank → your Evolve clearing account → bank account.
* Sync customer records (optional) so QuickBooks knows about Evolve customers.
* Provide a daily reconciliation report you can sign off on.
## Connect QuickBooks Online
{% stepper %}
{% step %}
### Authorize the app
In Evolve, **Settings → Integrations → QuickBooks → Connect**. You'll be redirected to QuickBooks Online to authorize. Pick the company file and approve.
{% endstep %}
{% step %}
### Map your accounts
The mapping page asks for one QuickBooks account per Evolve line type. Defaults are:
| Evolve line | QuickBooks account |
| --- | --- |
| Charge revenue | Sales Income |
| Processing fees | Bank Charges |
| Refunds | Returns and Allowances |
| Dispute losses | Bad Debt Expense |
| Evolve clearing | Other Current Asset (create if needed) |
You can override any of these to match your chart of accounts. See [Reconciliation mapping](reconciliation-mapping.md) for the deeper mapping options.
{% endstep %}
{% step %}
### Pick a sync schedule
Three options:
* **After each settlement** — journal entry posts to QB within minutes of the settlement file. Most teams use this.
* **Daily batch** — all entries for the prior day post at 8am local time.
* **On-demand** — entries queue up; you click **Sync now** to post.
{% endstep %}
{% step %}
### Verify the first sync
After the first settlement, check the journal entry in QuickBooks. Most teams have their accountant verify the first three days of entries before letting it run unattended. The Evolve dashboard shows a sync log under **Settings → Integrations → QuickBooks → History**.
{% endstep %}
{% endstepper %}
## Connect QuickBooks Desktop
QuickBooks Desktop doesn't have an OAuth API, so the integration uses an IIF file:
1. **Settings → Integrations → QuickBooks → Desktop → Configure**.
2. Pick the same account mapping as above.
3. Choose delivery method — daily email, SFTP push, or manual download from the dashboard.
4. In QuickBooks Desktop, import each day's IIF file. Most accounting teams script this with a Windows scheduled task.
For QuickBooks Desktop on a network with security restrictions, the SFTP-push option is the cleanest — Evolve pushes to your file server, your network does the rest.
## Customer sync
Optional. When enabled, Evolve customer records flow to QuickBooks Customer records on creation. Useful when your accountant needs per-customer revenue tracking inside QuickBooks (not just per-day aggregates).
Disable this if your QuickBooks file is already populated by your CRM — you'd end up with duplicates.
## Multi-currency
QuickBooks Online supports multi-currency on its higher-tier plans. Evolve's integration auto-detects whether your QB file has multi-currency enabled:
* **Multi-currency on** — each currency settles to its own clearing account; QB handles the FX gain/loss accounting.
* **Multi-currency off** — Evolve converts to your home currency at the daily mid-rate before posting, with the FX margin booked as a fee.
For Connect platforms with international sellers, multi-currency on is almost always the right choice.
## Last reviewed
Last reviewed in early 2026. Mapping conventions occasionally update with new QuickBooks releases; track changes via the [docs repo](https://github.com/GitbookIO/evolve-demo).
## Related
* [Reconciliation mapping](reconciliation-mapping.md) — the detailed account-mapping options.
* [Settlement files](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/settlement-files) — the source data the integration writes against.
* [NetSuite integration](../netsuite/README.md) — for higher-end accounting needs.
references/example-site/guides/integrations/quickbooks/reconciliation-mapping.md
---
icon: file-invoice
description: Map every Evolve settlement line item to the right QuickBooks account.
---
# Reconciliation mapping
The default account mapping covers most teams. For more nuanced setups — multi-class tracking, location-based accounting, project codes — the **Advanced mapping** view in **Settings → Integrations → QuickBooks → Mapping** has finer controls.
## Default mapping
Out of the box, Evolve maps settlement-file line types to QuickBooks accounts:
| Evolve line type | Default QuickBooks account | Account category |
| --- | --- | --- |
| `payment` (gross) | Sales Income | Income |
| `payment.fee` | Bank Charges | Expense |
| `refund` | Returns and Allowances | Income (contra) |
| `refund.fee_unrecovered` | Bank Charges | Expense |
| `dispute_lost` | Bad Debt Expense | Expense |
| `dispute_lost.fee` | Bank Charges | Expense |
| `dispute_won` | Other Income | Income |
| `payout` | Bank Account | Asset |
| `reserve_held` | Restricted Cash | Asset |
| `reserve_released` | Bank Account | Asset |
You override any of these in the mapping view.
## Per-class mapping
QuickBooks Online (Plus and above) supports **classes** — a tagging system for tracking revenue across product lines, regions, or projects. Evolve can tag each journal entry with a class derived from the charge's metadata:
```
Charge metadata: { product_line: "subscriptions" }
↓
QB journal entry class: "subscriptions"
```
In **Settings → Integrations → QuickBooks → Classes**, set which metadata key drives the class assignment. Multi-line businesses (commerce + subscriptions, US + EU, etc.) almost always want this enabled.
## Per-location mapping
QuickBooks Online's **locations** feature works similarly to classes, but for physical locations or business units. Map a metadata key to a QB location to track revenue per restaurant, per store, etc.
## Connect platforms
For Connect platforms, the mapping has an extra layer:
* **Application fees** earned by the platform → your platform's revenue account.
* **Transfers to sellers** → typically a clearing account, since the money isn't your revenue (it's the seller's).
* **Per-seller tracking** → optional, via class mapping per `connected_account_id`.
Most platforms with under 50 sellers track per-seller in QB; larger platforms track aggregated by tier or category.
## Reconciling against your bank
Each daily settlement creates one **payout** journal entry that should match exactly one credit on your bank statement. The Evolve dashboard shows a per-day match report:
* Posted to QB ✓
* Bank credit posted ✓
* Match status: matched / mismatched / pending
For mismatches, the report shows the difference (usually a timing issue between Evolve's posting and the bank's posting). Most discrepancies clear within a business day.
## Backfilling historical data
When you first connect QuickBooks, you can backfill journal entries for past settlements. Three options:
* **No backfill** — start with today's settlement going forward.
* **30 days back** — most common; matches the typical month-end-close timing.
* **Custom range** — useful for migrating from another accounting integration mid-year.
Backfills run in the background; you'll get an email when complete.
## Closing the books
For month-end close:
1. Confirm the last settlement of the month has posted to QB (usually the morning of the 1st).
2. Run the **Reconciliation summary** report from Evolve and compare to QB's balance.
3. Sign off on any flagged mismatches.
4. Close the period in QuickBooks.
The whole process for a typical month is 15–30 minutes for an accountant familiar with the integration.
## Last reviewed
Reviewed in early 2026. Mapping options expand occasionally with new QuickBooks features; suggest changes via [the docs repo](https://github.com/GitbookIO/evolve-demo).
## Related
* [QuickBooks overview](README.md) — install and concepts.
* [Settlement files](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/settlement-files) — the source data this writes against.
* [NetSuite custom field mapping](../netsuite/custom-field-mapping.md) — equivalent for NetSuite customers.
references/example-site/guides/integrations/README.md
---
icon: puzzle-piece
description: Connect Evolve to the tools you already use — Slack, Zapier, Segment, QuickBooks, NetSuite.
cover: .gitbook/assets/integrations-cover.png
coverY: 0
layout:
width: wide
tableOfContents:
visible: false
---
# Integrations
{% columns %}
{% column width="50%" %}
Evolve plugs into the tools your team already uses. Five officially-supported integrations cover the four jobs most teams need first — alerts, automation, customer-data routing, and accounting.
{% endcolumn %}
{% column width="50%" %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
Each integration is its own **page group** (Slack, Zapier, Segment, QuickBooks, NetSuite). Doc updates flow through **change requests** on GitHub — see the [evolve-pay/docs repo](https://github.com/GitbookIO/evolve-demo) for open PRs and the "Coming soon" section below for integrations being designed in the open right now.
{% endhint %}
{% endcolumn %}
{% endcolumns %}
## Featured: Slack
The most-installed integration on Evolve. Real-time alerts for disputes, refunds, and platform events; slash commands for looking up customers and charges without leaving Slack; per-channel routing rules so finance, ops, and engineering only see what they care about.
Most teams install Slack on day one and then add others as needs grow.
<p><a href="slack/README.md" class="button primary">Set up Slack</a> <a href="slack/configuring-alerts.md" class="button secondary">Configure alerts</a></p>
## Browse by job
### <i class="fa-bell" style="color:$primary;">:bell:</i> Real-time alerts and automation
Get notified when something happens, or trigger downstream actions in other tools.
* **[Slack](slack/README.md)** — channel-based alerts plus `/evolve` slash commands.
* **[Zapier](zapier/README.md)** — wire Evolve events to 6,000+ apps without writing code.
### <i class="fa-circle-nodes" style="color:$primary;">:circle-nodes:</i> Customer data
Stream Evolve activity into your data infrastructure.
* **[Segment](segment/README.md)** — Evolve as a source (event stream) or destination (customer-record updates).
### <i class="fa-calculator" style="color:$primary;">:calculator:</i> Accounting
Auto-create journal entries from settlements; sync customer records to your books.
* **[QuickBooks](quickbooks/README.md)** — Online and Desktop. Mapping per chart of accounts.
* **[NetSuite](netsuite/README.md)** — multi-subsidiary, custom segments, multi-currency.
## Coming soon
In active development. Each is being designed in the open via a GitHub pull request — comment to influence the design or sign up as a beta tester.
| Integration | What it does | Change request |
| --- | --- | --- |
| **HubSpot** | Sync Evolve customers to HubSpot contacts. | [PR #142](https://github.com/GitbookIO/evolve-demo) |
| **Pipedrive** | Evolve charges as Pipedrive deal events. | [PR #156](https://github.com/GitbookIO/evolve-demo) |
| **Xero** | Like QuickBooks, for Xero customers. | [PR #161](https://github.com/GitbookIO/evolve-demo) |
## Don't see what you need?
For tools not on this list, two paths:
* **Zapier**, if your tool integrates with it. [Our Zapier integration](zapier/README.md) is the fastest path — no engineering required.
* **Build your own**, against the [Webhooks API](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/). A few dozen lines of code wires Evolve to almost anything; the [tutorials](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/) cover common patterns.
references/example-site/guides/integrations/segment/event-mapping.md
---
icon: arrow-right-arrow-left
description: How Evolve events map to Segment track events and properties.
---
# Event mapping
Each Evolve event maps to a Segment track event with a curated subset of the event's data — flattened, dollar-denominated, and named to match Segment's spec.
## Identification
When an event has a customer attached, Evolve sends:
* `userId` — the customer's email if available, else the Evolve customer ID.
* `traits` — name, email, country, customer cohort, custom metadata fields.
For guest checkouts (no customer record), Evolve sends an `anonymousId` derived from the session ID instead.
## Payments events
| Evolve event | Segment event name | Key properties |
| --- | --- | --- |
| `charge.succeeded` | `Order Completed` | `revenue`, `currency`, `order_id`, `payment_method` |
| `charge.failed` | `Payment Failed` | `currency`, `decline_code`, `payment_method` |
| `charge.refunded` | `Order Refunded` | `revenue`, `currency`, `refund_amount` |
| `charge.disputed` | `Payment Disputed` | `revenue`, `currency`, `dispute_reason` |
| `payout.paid` | `Payout Sent` | `amount`, `currency`, `bank_last4` |
The `Order Completed` event matches the [Segment e-commerce spec](https://segment.com/docs/connections/spec/ecommerce/v2/), so it slots cleanly into Segment's pre-built downstream destinations (Mixpanel funnels, Customer.io campaigns, etc.).
## Customer events
| Evolve event | Segment event name | Key properties |
| --- | --- | --- |
| `customer.created` | `Customer Created` | `customer_id`, `email`, `signup_source` |
| `customer.updated` | `Customer Updated` | `customer_id`, `changed_fields` |
Customer events are also accompanied by an `identify` call to update the Segment user's traits.
## Subscription events
| Evolve event | Segment event name | Key properties |
| --- | --- | --- |
| `subscription.created` | `Subscription Started` | `plan`, `mrr`, `trial_end` |
| `subscription.invoice_paid` | `Subscription Renewed` | `plan`, `mrr` |
| `subscription.canceled` | `Subscription Canceled` | `plan`, `mrr_lost`, `cancel_reason` |
| `subscription.invoice_failed` | `Payment Failed` | `plan`, `mrr_at_risk`, `attempt_count` |
For SaaS-focused teams, the subscription mapping plus Mixpanel's pre-built MRR dashboards is the highest-leverage Segment integration.
## Identity events
| Evolve event | Segment event name | Key properties |
| --- | --- | --- |
| `verification_session.verified` | `Verification Completed` | `verification_type`, `customer_id` |
| `verification_session.failed` | `Verification Failed` | `verification_type`, `failure_reason` |
Useful for marketing tools that want to gate or personalize based on verification status.
## Connect events
| Evolve event | Segment event name | Key properties |
| --- | --- | --- |
| `account.verified` | `Seller Onboarded` | `account_id`, `country`, `business_type` |
| `account.restricted` | `Seller Restricted` | `account_id`, `restriction_reason` |
| `application_fee.created` | `Platform Revenue` | `amount`, `currency`, `seller_id`, `charge_id` |
The `Platform Revenue` event is the canonical "platform revenue per transaction" event — most marketplaces wire this directly to a daily revenue chart in their data warehouse.
## Custom metadata
Evolve resources carry a `metadata` object — arbitrary key/value pairs you attach. These flow into Segment as `metadata_*` properties on the relevant event. If your code adds `metadata: { campaign_id: "summer2026" }` to charges, the Segment event has `metadata_campaign_id: "summer2026"` for downstream attribution.
## Filtering events
If you only want a subset of events (e.g. only `charge.succeeded`), filter at the Evolve side under **Settings → Integrations → Segment → Source → Events**. This is more efficient than sending everything and filtering in Segment.
## Last reviewed
Last reviewed in early 2026. The mapping is stable; new events are added as new Evolve features ship — those go through change requests on the [docs repo](https://github.com/GitbookIO/evolve-demo).
## Related
* [Segment overview](README.md) — install and concepts.
* [Webhooks event catalog](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/event-catalog) — the Evolve-side events.
references/example-site/guides/integrations/segment/README.md
---
icon: circle-nodes
description: Stream Evolve events into Segment as a source, or apply Segment events to customer records.
---
# Segment
The Evolve Segment integration runs in two directions:
* **Source** — Evolve events flow into your Segment workspace, where they fan out to your downstream destinations (data warehouse, analytics, marketing tools).
* **Destination** — Segment events land on Evolve customer records as metadata, useful for personalizing receipts and surfacing context to support.
Most teams enable the source direction first (it's the higher-leverage one) and add the destination later if needed.
## Connect Evolve as a Segment source
{% stepper %}
{% step %}
### Get a Segment write key
In your Segment workspace, **Connections → Sources → Add Source → Evolve**. Segment generates a write key — copy it.
{% endstep %}
{% step %}
### Configure Evolve
In your Evolve dashboard, **Settings → Integrations → Segment → Source**. Paste the Segment write key and pick which events to stream:
* **Charges** (created, succeeded, failed, refunded, disputed)
* **Customers** (created, updated)
* **Subscriptions** (created, renewed, canceled)
* **Verifications** (created, verified, failed)
* **Connect** (account-related events)
The default selection covers the most common analytics use cases. Tighten it if you only need a subset.
{% endstep %}
{% step %}
### Verify in the Segment debugger
In Segment's source debugger, trigger a test charge in Evolve and watch the event arrive in Segment. From there, the data flows to whatever destinations you've configured (Snowflake, Mixpanel, Customer.io, etc.).
{% endstep %}
{% endstepper %}
## Source-direction event mapping
Each Evolve event becomes a Segment **track** event with a stable name and a curated payload. See [Event mapping](event-mapping.md) for the full table.
The general rule:
* The Evolve event ID is the Segment `messageId` — useful for deduplication.
* The Evolve customer email becomes the Segment `userId` (or `anonymousId` for guest checkouts).
* Amounts are in dollars (not cents), to match Segment conventions.
## Connect Segment as a destination
The destination direction lets Segment write to Evolve. Useful for:
* **Tagging** an Evolve customer with marketing-attribution data from Segment.
* **Triggering** an Evolve verification or refund from a Segment workflow.
In **Settings → Integrations → Segment → Destination**, configure which Segment events should affect Evolve records and how. Most teams use a small set of explicit mappings rather than firehosing everything.
{% hint style="warning" %}
**Be conservative with the destination direction.** Writes to Evolve from Segment are the most-likely place for data-quality issues — a misconfigured Segment source can pollute your Evolve customer records. Start with one event mapping, monitor the audit log, expand from there.
{% endhint %}
## Pricing
Segment integration is included on all plans. Per-event volume is metered against Segment's own pricing on their side; Evolve doesn't charge extra.
For the highest-volume mappings (every charge), Segment's per-MTU pricing can add up — watch your Segment usage as you scale.
## Last reviewed
Last reviewed in early 2026. Segment's event spec is occasionally updated; submit changes via the [docs repo](https://github.com/GitbookIO/evolve-demo).
## Related
* [Event mapping](event-mapping.md) — full Evolve→Segment event reference.
* [Webhooks](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/) — the underlying Evolve event source.
references/example-site/guides/integrations/slack/configuring-alerts.md
---
icon: bell
description: Decide which Evolve events fire alerts, where they go, and at what threshold.
---
# Configuring alerts
In **Settings → Integrations → Slack → Alerts**, you'll find the alert configuration. The default install enables a sensible starter set; most teams tune it within the first two weeks.
## Alert types
| Alert | Default channel | Threshold |
| --- | --- | --- |
| **Dispute opened** | `#evolve-alerts` | Always |
| **Large refund** | `#evolve-alerts` | Over $1,000 |
| **Payout failed** | `#evolve-alerts` | Always |
| **Risk hold placed** | `#evolve-alerts` | Always |
| **Account verification required** | `#evolve-alerts` | Always |
| **Connect: seller restricted** | `#evolve-alerts` | Always |
| **Daily summary** | (off) | n/a |
| **Weekly summary** | (off) | n/a |
Each alert can be:
* **Enabled or disabled** entirely.
* **Routed** to a different channel (any channel `@evolve` is invited to).
* **Filtered** by amount, country, or seller (where applicable).
## Routing patterns
A few patterns most teams use:
{% columns %}
{% column width="50%" %}
### <i class="fa-bell" style="color:$primary;">:bell:</i> Single channel
All Evolve alerts to one channel. Simplest. Fine for teams under 10 people.
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-route" style="color:$primary;">:route:</i> By function
`#finance-alerts` for payouts and disputes; `#support-alerts` for verifications and seller issues; `#eng-alerts` for webhook delivery failures.
{% endcolumn %}
{% endcolumns %}
{% columns %}
{% column width="50%" %}
### <i class="fa-globe" style="color:$primary;">:globe:</i> By region
Multi-region operations route alerts to per-region channels — `#evolve-us-alerts`, `#evolve-eu-alerts`, etc. Filter rules use the country on each event.
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-circles-overlap" style="color:$primary;">:circles-overlap:</i> Per-seller (Connect)
For platforms running Connect, route per-seller alerts to a channel shared with that seller (via Slack Connect). The seller sees their own disputes and payouts; you see all of them.
{% endcolumn %}
{% endcolumns %}
## Threshold tuning
For teams getting too many alerts, raise the thresholds:
* **Large refund threshold** — default $1,000. Raise to $5,000 if you process high-value transactions; below this won't fire.
* **Volume-spike alert** — fires when daily volume exceeds the rolling 30-day average by 50%. Useful for catching account compromise but noisy during sales events.
For the inverse problem (missing important alerts), check that your channel has the bot invited and that the filters aren't excluding the events you care about.
## Daily and weekly summaries
In addition to per-event alerts, Evolve can post scheduled digests to a channel:
* **Daily summary** — yesterday's volume, top customers, dispute rate. Posts at 9am in your account timezone.
* **Weekly summary** — past 7 days of metrics, week-over-week deltas, anomalies. Posts Monday morning.
Most teams enable the weekly summary in `#all-hands` or similar. Daily summary is more for finance / ops.
## Customizing the message format
The default alert messages are designed to be readable at a glance. To customize per-alert format (add fields, change the layout, attach metadata), use the **Custom alert templates** feature under **Settings → Integrations → Slack → Templates**.
Templates use a [block-kit-style format](https://api.slack.com/block-kit) with Evolve event variables. Most teams don't need to customize, but it's there if you want to add internal context (order ID, account manager name) to each alert.
## Pausing alerts during incidents
In an incident, you may want to pause Evolve alerts to avoid noise. Run `/evolve pause 1h` in any channel with the bot — that channel's alerts are paused for an hour. `/evolve resume` to bring them back.
The pause is per-channel and per-user; admins can also pause workspace-wide via the dashboard.
## Last reviewed
Reviewed in early 2026, with quarterly updates as new alert types ship. Suggest an alert type via [a change request on the docs repo](https://github.com/GitbookIO/evolve-demo).
## Related
* [Slack overview](README.md) — install and slash commands.
* [Webhooks event catalog](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/event-catalog) — the underlying event types these alerts surface.
references/example-site/guides/integrations/slack/README.md
---
icon: slack
description: Real-time alerts for disputes, refunds, and platform events. Slash commands for lookups.
---
# Slack
The Evolve Slack app sends real-time alerts to channels you pick and exposes slash commands for looking up customers, charges, and verification sessions without leaving Slack. Most teams install it once at platform launch and tweak the alert routing as needs evolve.
## What you can do
* **Get alerted** on disputes, large refunds, failed payouts, and account requirements changes.
* **Look up** a customer or charge by ID, email, or amount with `/evolve` slash commands.
* **Approve** Connect actions (manual reviews, payout releases) directly from Slack.
* **Schedule** daily and weekly digest summaries to a channel.
## Install
{% stepper %}
{% step %}
### Install the Slack app
In your Evolve dashboard, **Settings → Integrations → Slack → Install**. You'll be redirected to Slack to authorize the app for your workspace. Pick the workspace and click **Allow**.
{% endstep %}
{% step %}
### Pick the default channel
Pick a channel for general Evolve notifications. Most teams create a dedicated `#evolve-alerts` channel for this. The bot needs to be invited to any channel you want to send notifications to — the install flow does this automatically for the default channel; for additional channels, run `/invite @evolve` in the channel.
{% endstep %}
{% step %}
### Configure alerts
The default install enables the most common alerts (disputes, failed payouts). To customize which events go where, see [Configuring alerts](configuring-alerts.md).
{% endstep %}
{% endstepper %}
## Slash commands
Once installed, anyone in your workspace with access to the channel can use:
| Command | What it does |
| --- | --- |
| `/evolve customer cus_123` | Show a customer's record, recent charges, balance |
| `/evolve charge ch_123` | Show a charge with its full timeline |
| `/evolve search jordan@acme.com` | Search across customers, charges, verifications |
| `/evolve dispute dp_123` | Show a dispute and let you upload evidence |
| `/evolve verify vs_123` | Show a verification session and its check results |
| `/evolve help` | Full list of commands |
Slash command results are visible only to the user who ran them — they're not posted to the channel.
## Permissions
The Slack app uses scoped permissions:
* **Read** — anyone in a channel where Evolve is installed can see incoming alerts and run lookup slash commands.
* **Approve** — only members of your **#evolve-approvers** group (configurable) can approve manual reviews or release held payouts.
The mapping between Slack users and Evolve dashboard users is automatic when the email matches. For Slack users without matching dashboard accounts, slash commands return a "no access" message rather than data.
## Last reviewed
This page was last reviewed in early 2026. Doc updates flow through change requests in the [evolve-pay/docs repo](https://github.com/GitbookIO/evolve-demo) — open one if anything here is out of date.
## Related
* [Configuring alerts](configuring-alerts.md) — picking which events go where.
* [Webhooks](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/) — the underlying event source.
* [Disputes at scale](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/marketplace/disputes-at-scale) — the most-common dispute-routing pattern.
references/example-site/guides/integrations/SUMMARY.md
# Table of contents
* [Integrations](README.md)
## Slack
* [Overview](slack/README.md)
* [Configuring alerts](slack/configuring-alerts.md)
## Zapier
* [Overview](zapier/README.md)
* [Triggers and actions](zapier/triggers-and-actions.md)
## Segment
* [Overview](segment/README.md)
* [Event mapping](segment/event-mapping.md)
## QuickBooks
* [Overview](quickbooks/README.md)
* [Reconciliation mapping](quickbooks/reconciliation-mapping.md)
## NetSuite
* [Overview](netsuite/README.md)
* [Custom field mapping](netsuite/custom-field-mapping.md)
references/example-site/guides/integrations/zapier/README.md
---
icon: bolt-lightning
description: Wire Evolve events to 6,000+ apps without writing code.
---
# Zapier
The Evolve Zapier integration lets you connect Evolve to anything Zapier supports — Google Sheets, Mailchimp, Notion, Airtable, Salesforce, you name it. Triggers fire when Evolve events happen; actions let you create Evolve records from other apps.
This is the right path when:
* You don't have engineering bandwidth to build a webhook integration.
* The tool you want to connect to is already in Zapier's catalog.
* The volume is reasonable (Zapier's free tier covers 100 tasks/month; paid plans go up from there).
For high-volume integrations, build directly against [webhooks](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/) — Zapier's per-task pricing adds up fast.
## Connect Evolve in Zapier
{% stepper %}
{% step %}
### Authenticate
In Zapier, search for "Evolve" and click **Connect**. You'll be asked for your Evolve API key — paste a [restricted key](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/authentication#restricted-keys) scoped to read-only or to the specific operations the Zap will perform.
For most Zaps, **Read-only** is enough. For Zaps that create or refund charges, scope to the specific resources.
{% endstep %}
{% step %}
### Pick a trigger
Zaps start with a trigger — an event in Evolve that kicks the Zap off. The full list is on [Triggers and actions](triggers-and-actions.md). Common starting points:
* **New charge succeeded** — for tracking sales in a spreadsheet.
* **New dispute opened** — for routing to a support tool like Intercom.
* **New customer created** — for syncing to a CRM.
{% endstep %}
{% step %}
### Add downstream actions
After the trigger, chain whatever Zapier supports. Examples:
* New charge → append to Google Sheets.
* New dispute → create a Notion task.
* New customer → add to Mailchimp.
* Failed payout → send Slack DM to the finance team.
{% endstep %}
{% step %}
### Test and turn on
Zapier's **Test** step pulls a real recent event from your Evolve test mode and runs the Zap. Verify the downstream action looks right. When you turn the Zap on, it starts processing live events.
{% endstep %}
{% endstepper %}
## Live mode vs test mode
The API key you connect determines which mode the Zap reads from. Use a `sk_test_*` key during build-out; swap to `sk_live_*` when ready. Most teams keep two Zaps in parallel — one against test for development, one against live for production.
## Common Zap recipes
A handful of patterns worth copying:
| Recipe | Trigger | Action |
| --- | --- | --- |
| **Sales tracker** | Charge succeeded | Append row to Google Sheets |
| **Dispute alerter** | Dispute opened | Create Linear issue |
| **CRM sync** | Customer created | Create HubSpot contact |
| **Refund logger** | Refund created | Post to Slack #refunds |
| **Onboarding email** | Connect account verified | Send Mailchimp welcome email |
## Limits
* Zapier's free tier: 100 tasks/month, 5 Zaps active at once. Fine for prototyping; you'll outgrow it.
* Zapier's paid tiers handle 10,000+ tasks/month if you need them.
* Zapier polls our API every 1–15 minutes depending on plan, so triggers aren't real-time. For sub-second reactivity, use webhooks.
## Last reviewed
Last reviewed in early 2026. Zapier's catalog of supported triggers and actions is occasionally updated; suggest additions via [change request](https://github.com/GitbookIO/evolve-demo).
## Related
* [Triggers and actions](triggers-and-actions.md) — the full catalog.
* [Webhooks](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/) — for the engineering-driven path.
* [Authentication → Restricted keys](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/authentication#restricted-keys) — the right key shape for Zapier.
references/example-site/guides/integrations/zapier/triggers-and-actions.md
---
icon: list-check
description: Every Evolve trigger and action available in Zapier, with sample payloads.
---
# Triggers and actions
The Zapier integration exposes a curated subset of Evolve's API — the events most useful for cross-tool automation, plus the actions most useful from external apps.
## Triggers
Triggers fire when something happens in Evolve. Each maps to a real-time webhook event under the hood, with Zapier polling on top.
### Payments triggers
| Trigger | Fires when | Common downstream action |
| --- | --- | --- |
| Charge succeeded | A charge captures successfully | Append to spreadsheet, update CRM |
| Charge failed | A charge fails (declined or processing error) | Alert in Slack, log in Linear |
| Refund created | A refund is issued | Email customer, update internal records |
| Dispute opened | A new dispute lands | Create Linear/Jira ticket, alert ops |
| Payout paid | A payout completes to your bank | Update accounting spreadsheet |
### Identity triggers
| Trigger | Fires when |
| --- | --- |
| Verification verified | An identity verification succeeds |
| Verification failed | An identity verification fails |
| Verification needs review | An automated check is inconclusive |
| Bank account verified | A Plaid or micro-deposit verification succeeds |
### Connect triggers
| Trigger | Fires when |
| --- | --- |
| Connected account verified | A new seller completes onboarding |
| Account requirements updated | A seller needs to provide more info |
| Application fee earned | Your platform earns a fee on a charge |
| Connect dispute opened | A dispute opens against a seller's charge |
### Customer triggers
| Trigger | Fires when |
| --- | --- |
| Customer created | A new customer record is created |
| Customer updated | An existing customer's details change |
## Actions
Actions let other apps create things in Evolve. Use a [restricted key](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/authentication#restricted-keys) scoped to just the actions your Zap performs.
### Customer actions
* **Create customer** — useful when a new lead in your CRM should also exist as an Evolve customer.
* **Update customer** — sync metadata changes from your CRM.
* **Tag customer** — add a metadata key/value pair.
### Charge actions
* **Create charge** — for very narrow use cases. Most teams shouldn't trigger charges from Zaps; the failure modes are hard to debug.
* **Refund charge** — useful for one-off refund flows where a support tool triggers the refund.
### Verification actions
* **Create verification session** — kick off an identity check from another app.
### Connect actions
* **Create connected account** — useful for integrating a non-Zapier-sourced seller list (e.g. a custom CRM with sellers you want to onboard).
## Sample trigger payload
When a charge-succeeded trigger fires, Zapier receives:
```json
{
"id": "ch_3KsM12pL9qXa7",
"amount_dollars": 42.00,
"currency": "USD",
"status": "succeeded",
"customer_id": "cus_4n2P3qR5sT6uV",
"customer_email": "jordan@acme.com",
"description": "Order #1042",
"created_at": "2026-04-30T14:22:01Z",
"metadata": { "order_id": "1042" }
}
```
The fields are flattened (Zapier handles them better than nested JSON) and amounts are in dollars rather than cents. Use these directly in downstream actions.
## Custom fields
Each Evolve resource carries a `metadata` object — arbitrary key/value pairs your code attaches to the resource. These are surfaced in Zapier as individual fields you can map. If you tag every charge with `order_id`, that field appears as `metadata_order_id` in your Zap step.
## Limits and best practices
* **Test mode first.** Build with a `sk_test_*` key, swap to live when ready.
* **Use restricted keys.** Don't paste a full secret key into Zapier — scope it.
* **Watch task usage.** Trigger frequency × Zap count adds up fast on Zapier's per-task pricing. Budget accordingly.
* **For real-time, use webhooks.** Zapier polls every 1–15 minutes depending on plan. Sub-minute reactivity needs a direct webhook integration.
## Last reviewed
Last reviewed in early 2026. The trigger and action catalog is updated as new Evolve features ship; suggest additions via [change request on the docs repo](https://github.com/GitbookIO/evolve-demo).
references/example-site/guides/tutorials/.gitbook/vars.yaml
api_live: https://api.evolve.com
api_test: https://api.test.evolve.com
dashboard_live: https://dashboard.evolve.com
dashboard_test: https://dashboard.test.evolve.com
support_email: support@evolve.com
status_page: https://status.evolve.com
tutorial_video: https://www.youtube.com/watch?v=55oOB-lsQKY
references/example-site/guides/tutorials/marketplace/custom-onboarding.md
---
icon: code
description: Build seller onboarding entirely in your own UI — programmatic field collection without the hosted flow.
---
# Build a custom Connect onboarding flow
By the end of this tutorial you'll have a fully white-labeled seller onboarding flow — your own forms, your own URL, your own branding — that programmatically submits seller data to Evolve and handles the verification responses. The build takes about 4 hours.
This is for platforms with strong brand standards or specific UX needs the hosted flow doesn't cover. Most platforms shouldn't take this path — it's more work and the hosted flow keeps pace with regulatory changes automatically. But for platforms where it matters, here's how.
{% hint style="warning" %}
**Custom onboarding is more compliance-sensitive than hosted.** With hosted onboarding, Evolve handles regional variations and regulatory updates automatically. With custom, your code is on the hook. Make sure you have someone on your team who can keep up with KYC rule changes.
{% endhint %}
{% hint style="info" %}
**Prerequisites.** [Onboard your first sellers](onboard-sellers.md) finished — to understand what the hosted flow does. Enterprise account (custom onboarding is Enterprise-only). Your platform has a designer and at least one engineer with PCI-aware experience.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Create the connected account in custom mode
Pass `onboarding_mode: "custom"` so Evolve doesn't generate a hosted URL:
{% tabs %}
{% tab title="Node" %}
```js
const account = await evolve.connect.connectedAccounts.create({
type: "individual",
country: "US",
email: seller.email,
onboarding_mode: "custom",
metadata: { internal_seller_id: seller.id },
});
await db.sellers.update(seller.id, {
evolve_account_id: account.id,
onboarding_status: "started",
});
```
{% endtab %}
{% tab title="Python" %}
```python
account = evolve.ConnectedAccount.create(
type="individual",
country="US",
email=seller.email,
onboarding_mode="custom",
metadata={"internal_seller_id": seller.id},
)
db.sellers.update(
seller.id,
evolve_account_id=account.id,
onboarding_status="started",
)
```
{% endtab %}
{% tab title="cURL" %}
```bash
curl https://api.evolve.com/v2/connected_accounts \
-H "Authorization: Bearer $EVOLVE_SECRET_KEY" \
-d type=individual \
-d country=US \
-d email=seller@example.com \
-d onboarding_mode=custom \
-d metadata[internal_seller_id]=42
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Submit the legal info
Build your own form for legal name, DOB, address, and tax ID. Submit each as you collect it (or in one batch):
{% tabs %}
{% tab title="Node" %}
```js
await evolve.connect.connectedAccounts.update(account.id, {
individual: {
first_name: form.firstName,
last_name: form.lastName,
dob: form.dob, // "1985-04-12"
address: form.address,
ssn_last_4: form.ssnLast4,
},
});
```
{% endtab %}
{% tab title="Python" %}
```python
evolve.ConnectedAccount.modify(
account.id,
individual={
"first_name": form["first_name"],
"last_name": form["last_name"],
"dob": form["dob"],
"address": form["address"],
"ssn_last_4": form["ssn_last_4"],
},
)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Capture identity documents (the hard part)
This is where custom onboarding gets sensitive. You need to collect a government ID and a selfie, and you need to do it without your servers ever touching the raw image — that would put you in PCI-equivalent compliance scope for ID data.
Use Evolve's secure-upload widget, which embeds a managed upload UI in an iframe. The image goes from the customer's browser straight to Evolve's servers; your code never sees the bytes:
```html
<div id="evolve-id-upload"></div>
<script src="https://js.evolve.com/v1/identity.js"></script>
<script>
const evolve = Evolve('pk_live_...');
evolve.mountIdentityUpload('#evolve-id-upload', {
accountId: 'acct_3KsM12pL9q',
onComplete: (result) => {
// result.documentId is the reference; submit to your server
submitToServer(result.documentId);
},
});
</script>
```
Your server receives the document ID and attaches it to the connected account:
{% tabs %}
{% tab title="Node" %}
```js
await evolve.connect.connectedAccounts.update(account.id, {
individual: {
verification: {
document: documentId,
},
},
});
```
{% endtab %}
{% tab title="Python" %}
```python
evolve.ConnectedAccount.modify(
account.id,
individual={"verification": {"document": document_id}},
)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Submit bank info via Plaid Link
Same iframe-based pattern for the bank account. Mount Evolve's Plaid widget; your server receives the verified bank account ID:
```html
<div id="evolve-bank"></div>
<script>
evolve.mountBankVerification('#evolve-bank', {
accountId: 'acct_3KsM12pL9q',
onComplete: (result) => {
submitBankToServer(result.bankVerificationId);
},
});
</script>
```
For sellers without a Plaid-supported bank, you can collect routing + account directly in your form (no PCI scope for ACH numbers) and submit:
{% tabs %}
{% tab title="Node" %}
```js
await evolve.connect.connectedAccounts.update(account.id, {
external_account: {
object: "bank_account",
country: "US",
currency: "usd",
routing_number: form.routingNumber,
account_number: form.accountNumber,
},
});
```
{% endtab %}
{% tab title="Python" %}
```python
evolve.ConnectedAccount.modify(
account.id,
external_account={
"object": "bank_account",
"country": "US",
"currency": "usd",
"routing_number": form["routing_number"],
"account_number": form["account_number"],
},
)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Trigger the verification
Once all required fields are submitted, request verification:
{% tabs %}
{% tab title="Node" %}
```js
await evolve.connect.connectedAccounts.requestVerification(account.id);
```
{% endtab %}
{% tab title="Python" %}
```python
evolve.ConnectedAccount.request_verification(account.id)
```
{% endtab %}
{% endtabs %}
The same `account.verified` / `account.requirements_updated` webhooks fire as in hosted mode.
{% endstep %}
{% step %}
### Handle requirements updates in your UI
When `account.requirements_updated` fires, your code parses `requirements.currently_due` and shows the seller exactly which fields to fix:
{% tabs %}
{% tab title="Node" %}
```js
function buildResumeFlow(account) {
const due = account.requirements.currently_due;
const steps = [];
if (due.includes("individual.verification.document")) steps.push("upload_id");
if (due.includes("individual.address")) steps.push("address");
if (due.includes("external_account")) steps.push("bank_account");
return steps;
}
```
{% endtab %}
{% tab title="Python" %}
```python
def build_resume_flow(account):
due = account.requirements["currently_due"]
steps = []
if "individual.verification.document" in due:
steps.append("upload_id")
if "individual.address" in due:
steps.append("address")
if "external_account" in due:
steps.append("bank_account")
return steps
```
{% endtab %}
{% endtabs %}
This is the part of custom onboarding that's most ongoing-maintenance work — `currently_due` field names occasionally change as Evolve adds new requirements (especially internationally). Plan for quarterly review.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Why did our PCI scope go up after switching to custom?"</summary>
It shouldn't have, if you're using the secure-upload widgets for ID documents. If you accidentally collected ID images on your own server, you're now in document-data compliance scope. Migrate to the iframe widget immediately — the bytes shouldn't have hit your server in the first place.
</details>
<details>
<summary>"Custom onboarding works in the US but breaks for international sellers"</summary>
International sellers have different required fields per country. Check `requirements.currently_due` per account; the names vary (`individual.id_number` for some countries, `individual.verification.document.front` for others). Build your form dynamically off the requirements list rather than hardcoding fields.
</details>
<details>
<summary>"Should we even be doing custom onboarding?"</summary>
Honestly, probably not unless: (1) your brand standards require it, (2) you have a compliance team that can keep up with KYC changes, (3) your seller cohort is concentrated in one country. The hosted flow handles regulatory variations automatically; custom is more work for the same outcome in most cases.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-user-plus" style="color:$primary;">:user-plus:</i></h3></td><td><strong>Hosted onboarding</strong></td><td>Compare against the hosted approach.</td><td><a href="onboard-sellers.md">onboard-sellers.md</a></td></tr><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Split payments</strong></td><td>Once sellers are verified, split each payment.</td><td><a href="split-payments.md">split-payments.md</a></td></tr><tr><td><h3><i class="fa-briefcase" style="color:$primary;">:briefcase:</i></h3></td><td><strong>Verify a business (KYB)</strong></td><td>For company sellers, the same custom-onboarding logic plus KYB.</td><td><a href="../verification/kyb.md">kyb.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/marketplace/disputes-at-scale.md
---
icon: gavel
description: Route disputes to the responsible seller, gather evidence automatically, and protect your platform's dispute rate.
---
# Handle disputes and refunds at scale
By the end of this tutorial you'll have a multi-seller dispute management flow that delegates evidence collection to sellers, automates evidence gathering where possible, and keeps your platform-level dispute rate below the network thresholds. The build takes about 2 hours.
This is one of the most operationally complex parts of running a marketplace at scale. The platform is the merchant of record for card-network purposes — meaning the platform's dispute rate is what the networks watch, regardless of which seller caused the dispute.
{% hint style="info" %}
**Prerequisites.** [Onboard sellers](onboard-sellers.md) and [Split payments](split-payments.md) finished. Read [Connect → Refunds and disputes](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/platform-setup/refunds-and-disputes) for the model.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Decide your dispute policy
Three policies for who absorbs the dispute:
* **Pass to seller** — seller's balance covers the disputed amount + fee. Most common.
* **Platform absorbs** — platform balance covers everything. For premium-tier sellers as a perk.
* **Split** — platform takes the $15 fee, seller takes the disputed amount. Some platforms use this as a middle-ground.
Set the platform default in **Connect → Settings → Dispute policy**. Override per-seller as needed.
{% endstep %}
{% step %}
### Subscribe to dispute events
Two key events:
* `dispute.created` — a new dispute opened. Funds are withdrawn from the responsible balance immediately.
* `dispute.evidence_required` — reminder that the response window is closing.
{% endstep %}
{% step %}
### Route dispute notifications to the seller
Most platforms delegate evidence collection to the seller. When a dispute opens against one of their charges:
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "dispute.created") {
const dispute = event.data.object;
const seller = await db.sellers.findOne({
evolve_account_id: dispute.connected_account,
});
await sendEmail(seller.email, "dispute_opened", {
disputeId: dispute.id,
amount: dispute.amount,
reasonCode: dispute.reason,
deadline: dispute.evidence_due_by,
portalUrl: `https://yourapp.com/disputes/${dispute.id}`,
});
await db.disputes.create({
evolve_dispute_id: dispute.id,
seller_id: seller.id,
status: "awaiting_seller",
deadline: dispute.evidence_due_by,
});
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "dispute.created":
dispute = event.data.object
seller = db.sellers.find_by_account(dispute.connected_account)
send_email(
seller.email,
"dispute_opened",
dispute_id=dispute.id,
amount=dispute.amount,
reason_code=dispute.reason,
deadline=dispute.evidence_due_by,
portal_url=f"https://yourapp.com/disputes/{dispute.id}",
)
db.disputes.create(
evolve_dispute_id=dispute.id,
seller_id=seller.id,
status="awaiting_seller",
deadline=dispute.evidence_due_by,
)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Build a seller-facing dispute portal
Sellers shouldn't see the Evolve dashboard directly — they see your portal. Build a per-dispute page where they can:
1. Read the dispute details (reason code, customer's claim, deadline).
2. Upload evidence — shipping receipts, customer-communication logs, signed agreements.
3. Choose to fight or accept.
4. Submit.
Auto-collect what you can — for `product_not_received` disputes on shipped goods, your fulfillment system already has the tracking number. Pre-fill the response.
{% endstep %}
{% step %}
### Submit evidence to Evolve
When the seller submits, your code packages it and sends to Evolve:
{% tabs %}
{% tab title="Node" %}
```js
app.post("/disputes/:id/respond", async (req, res) => {
const dispute = await db.disputes.findOne(req.params.id);
await evolve.disputes.update(dispute.evolve_dispute_id, {
evidence: {
product_description: req.body.productDescription,
shipping_carrier: req.body.carrier,
shipping_tracking_number: req.body.trackingNumber,
customer_communication: req.body.commsLog,
},
submit: req.body.action === "submit",
});
await db.disputes.update(dispute.id, { status: "submitted" });
res.json({ ok: true });
});
```
{% endtab %}
{% tab title="Python" %}
```python
@app.post("/disputes/<id>/respond")
def respond(id):
dispute = db.disputes.find(id)
evolve.Dispute.modify(
dispute.evolve_dispute_id,
evidence={
"product_description": request.json["product_description"],
"shipping_carrier": request.json["carrier"],
"shipping_tracking_number": request.json["tracking_number"],
"customer_communication": request.json["comms_log"],
},
submit=request.json["action"] == "submit",
)
db.disputes.update(dispute.id, status="submitted")
return {"ok": True}
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Set a default-action policy for non-responsive sellers
Some sellers won't respond. Your platform needs a default action when the deadline approaches with no submission:
{% tabs %}
{% tab title="Node" %}
```js
async function handleEvidenceDeadline(dispute) {
if (dispute.status === "awaiting_seller") {
if (config.default_action === "accept") {
await evolve.disputes.accept(dispute.evolve_dispute_id);
} else if (config.default_action === "submit_minimal") {
await evolve.disputes.update(dispute.evolve_dispute_id, {
evidence: { product_description: "Order auto-fulfilled per platform records." },
submit: true,
});
}
}
}
```
{% endtab %}
{% tab title="Python" %}
```python
def handle_evidence_deadline(dispute):
if dispute.status == "awaiting_seller":
if config.default_action == "accept":
evolve.Dispute.accept(dispute.evolve_dispute_id)
elif config.default_action == "submit_minimal":
evolve.Dispute.modify(
dispute.evolve_dispute_id,
evidence={"product_description": "Order auto-fulfilled per platform records."},
submit=True,
)
```
{% endtab %}
{% endtabs %}
For most marketplaces, "submit minimal" produces better outcomes than "accept" — at least the network knows you tried.
{% endstep %}
{% step %}
### Monitor per-seller dispute rates
A small number of bad sellers can drag down the platform's dispute rate. Build a daily report:
* Per-seller dispute rate (disputes / charges, last 90 days).
* Sellers above 1% — auto-flag for review.
* Sellers above 1.5% — auto-pause.
Use the Connect API to retrieve dispute stats per connected account, or pull the per-seller report and calculate yourself.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Our platform-level dispute rate is fine but rising"</summary>
Pull a per-seller breakdown and look for the bottom 10% of sellers (by dispute rate). Most platforms find that 5% of sellers cause 50% of disputes. Tighten onboarding requirements, add a dispute-rate clause to your seller terms, and apply per-seller reserves to the high-risk cohort.
</details>
<details>
<summary>"A seller's dispute rate is high but they bring high revenue"</summary>
Hard call. Some platforms maintain a "high-risk cohort" with explicit risk pricing — these sellers pay a higher take rate that covers the expected dispute losses. Others terminate the cohort entirely. Decide based on your unit economics; whatever you do, make the policy explicit in your seller terms.
</details>
<details>
<summary>"Sellers complain that legitimate sales are getting disputed"</summary>
Some of this is unavoidable. The biggest preventable category is `unrecognized` disputes — fix the statement descriptor (see [Prevent chargebacks](../payment-flows/chargeback-prevention.md)). For `product_not_received`, make sure tracking is recorded automatically — sellers who manually-attach tracking forget half the time.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Prevent chargebacks</strong></td><td>The platform-wide chargeback playbook.</td><td><a href="../payment-flows/chargeback-prevention.md">chargeback-prevention.md</a></td></tr><tr><td><h3><i class="fa-money-bill-transfer" style="color:$primary;">:money-bill-transfer:</i></h3></td><td><strong>Per-seller payout schedules</strong></td><td>Reserves and dispute holds.</td><td><a href="payout-schedules.md">payout-schedules.md</a></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>3-D Secure</strong></td><td>Liability shift on the disputable charges.</td><td><a href="../payment-flows/3d-secure.md">3d-secure.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/marketplace/onboard-sellers.md
---
icon: user-plus
description: Onboard sellers onto your marketplace with the hosted Connect onboarding flow.
---
# Onboard your first sellers
By the end of this tutorial you'll have a working seller onboarding flow — your platform sends an invite, the seller completes a hosted form (legal info, identity, banking), and they're ready to take payments through your marketplace. The build takes about 90 minutes.
This is the right starting point for any marketplace, B2B platform, or vertical SaaS that takes payments on behalf of customers.
{% hint style="info" %}
**Prerequisites.** A Growth or Enterprise account with Connect enabled. Test API keys. Read [Connect → Onboarding sellers](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/platform-setup/onboarding-sellers) for the conceptual model.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Create a connected account
When a seller signs up to your marketplace, your server creates a connected account and gets back a hosted onboarding URL:
{% tabs %}
{% tab title="Node" %}
```js
const account = await evolve.connect.connectedAccounts.create({
type: "individual",
country: "US",
email: req.body.sellerEmail,
metadata: {
internal_seller_id: seller.id,
},
});
await db.sellers.update(seller.id, {
evolve_account_id: account.id,
onboarding_status: "pending",
});
res.json({ onboardingUrl: account.onboarding_url });
```
{% endtab %}
{% tab title="Python" %}
```python
account = evolve.ConnectedAccount.create(
type="individual",
country="US",
email=request.json["seller_email"],
metadata={"internal_seller_id": seller.id},
)
db.sellers.update(
seller.id,
evolve_account_id=account.id,
onboarding_status="pending",
)
return jsonify(onboardingUrl=account.onboarding_url)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Send the seller to the hosted flow
The hosted flow walks the seller through:
1. Personal info (legal name, DOB, address).
2. Government-issued ID (document + selfie).
3. Bank account for payouts (Plaid or micro-deposits).
4. Tax form (W-9 for US individuals, W-8 for international).
Most US individuals complete in under 10 minutes.
{% endstep %}
{% step %}
### Listen for the verification webhook
The full account-verified webhook fires when all the steps are done:
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "account.verified") {
const account = event.data.object;
await db.sellers.update(
{ evolve_account_id: account.id },
{
onboarding_status: "verified",
can_charge: account.capabilities.charges_enabled,
can_payout: account.capabilities.payouts_enabled,
}
);
notifySellerActivated(account.metadata.internal_seller_id);
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "account.verified":
account = event.data.object
db.sellers.update_where(
evolve_account_id=account.id,
values={
"onboarding_status": "verified",
"can_charge": account.capabilities["charges_enabled"],
"can_payout": account.capabilities["payouts_enabled"],
},
)
notify_seller_activated(account.metadata["internal_seller_id"])
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Surface the seller's status in your UI
Sellers have one of five states. Show this clearly in their dashboard:
| State | Meaning | What seller can do |
| --- | --- | --- |
| `pending` | Onboarding link sent, not yet started | Click the link |
| `incomplete` | Started, missing some required info | Resume onboarding |
| `processing` | Submitted, Evolve is reviewing | Nothing — wait |
| `verified` | All checks passed | Take payments, receive payouts |
| `restricted` | Risk team has restricted the account | Contact you for next steps |
Provide a "Resume onboarding" button for `incomplete` accounts that re-generates a hosted URL.
{% endstep %}
{% step %}
### Handle requirement updates
Onboarding occasionally needs more info — a clearer ID photo, a tax form, address verification. When this happens, the `account.requirements_updated` event fires:
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "account.requirements_updated") {
const account = event.data.object;
if (account.requirements.currently_due.length > 0) {
notifySeller(
account.metadata.internal_seller_id,
"additional_info_needed",
{ url: account.onboarding_url }
);
}
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "account.requirements_updated":
account = event.data.object
if account.requirements["currently_due"]:
notify_seller(
account.metadata["internal_seller_id"],
"additional_info_needed",
url=account.onboarding_url,
)
```
{% endtab %}
{% endtabs %}
The `onboarding_url` on the account is always valid — it deep-links the seller back to whatever's missing.
{% endstep %}
{% step %}
### Test the verification fixtures
Test mode includes fixtures for each end-state:
| Email used | Result |
| --- | --- |
| `pass@example.com` | Verifies on first submit |
| `manual_review@example.com` | Goes to manual review (admin must approve in dashboard) |
| `requirements_due@example.com` | Onboards with `currently_due` populated |
| `restricted@example.com` | Restricted (risk-flagged) |
Walk through each. Confirm your UI handles all four states.
{% endstep %}
{% step %}
### Pre-fill what you already know
Most platforms collect some of the seller's info during sign-up. Pre-fill it:
{% tabs %}
{% tab title="Node" %}
```js
const account = await evolve.connect.connectedAccounts.create({
type: "individual",
country: "US",
email: seller.email,
prefill: {
legal_name: seller.full_name,
address: seller.business_address,
dob: seller.date_of_birth,
},
metadata: { internal_seller_id: seller.id },
});
```
{% endtab %}
{% tab title="Python" %}
```python
account = evolve.ConnectedAccount.create(
type="individual",
country="US",
email=seller.email,
prefill={
"legal_name": seller.full_name,
"address": seller.business_address,
"dob": seller.date_of_birth,
},
metadata={"internal_seller_id": seller.id},
)
```
{% endtab %}
{% endtabs %}
The seller can still edit; pre-fill just saves typing.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Sellers don't return after we send the email"</summary>
Most-cited cause: the email looked transactional and got buried. Send it from a recognizable name (not `noreply@`), use a clear subject ("Finish setting up your seller account"), and put the CTA button above the fold. After 24 hours, send a follow-up. After 7 days, archive the lead — onboarding-incomplete sellers rarely resurrect.
</details>
<details>
<summary>"My seller's account is `verified` but `payouts_enabled` is false"</summary>
That means identity passed but bank verification didn't. Check `requirements.currently_due` — usually they need to complete the bank step (Plaid auth failed or micro-deposits never confirmed). Send them back to the onboarding URL.
</details>
<details>
<summary>"How do I verify business sellers (LLCs, corporations)?"</summary>
Use `type: "company"` instead of `"individual"`. The flow extends with KYB — beneficial-ownership collection and business sanctions screening. See [Verify a business (KYB)](../verification/kyb.md) for the deeper walkthrough.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Split each payment</strong></td><td>Application fees and conditional rules.</td><td><a href="split-payments.md">split-payments.md</a></td></tr><tr><td><h3><i class="fa-money-bill-transfer" style="color:$primary;">:money-bill-transfer:</i></h3></td><td><strong>Per-seller payout schedules</strong></td><td>Daily, weekly, monthly, on-demand.</td><td><a href="payout-schedules.md">payout-schedules.md</a></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>Custom onboarding flow</strong></td><td>When the hosted flow isn't enough.</td><td><a href="custom-onboarding.md">custom-onboarding.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/marketplace/payout-schedules.md
---
icon: money-bill-transfer
description: Configure how often each seller gets paid — daily, weekly, monthly, on demand.
---
# Configure per-seller payout schedules
By the end of this tutorial you'll have a payout system where sellers can pick their own schedule (within the limits you set), where on-demand payouts are available for fast-cash sellers, and where reserves apply automatically to high-risk cohorts. The build takes about 60 minutes.
This is the seller-cash-flow side of Connect — the schedule directly affects seller satisfaction and is one of the most-asked-about features by sellers themselves.
{% hint style="info" %}
**Prerequisites.** [Onboard your first sellers](onboard-sellers.md) and [Split payments](split-payments.md) finished. At least one verified connected account with a successful test payment.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Pick the platform default
In **Connect → Settings → Default payout schedule**, set the default for new sellers. Three patterns we see most:
* **Daily** — best for cash-flow-sensitive sellers (gig economy, delivery). Highest seller satisfaction.
* **Weekly** — best for SaaS marketplaces and B2B platforms. Aligns with seller's invoicing cadence.
* **Monthly** — best for high-margin businesses where seller cash flow doesn't drive churn.
Pick the one that matches your typical seller. Override per-seller as needed.
{% endstep %}
{% step %}
### Let sellers override
Sellers may have a strong preference. Surface a control in their dashboard:
{% tabs %}
{% tab title="Node" %}
```js
app.post("/sellers/:id/payout-schedule", async (req, res) => {
const seller = await db.sellers.findOne(req.params.id);
const allowedSchedules = ["daily", "weekly", "monthly"];
if (!allowedSchedules.includes(req.body.schedule)) {
return res.status(400).json({ error: "Invalid schedule" });
}
await evolve.connect.connectedAccounts.update(seller.evolve_account_id, {
payout_schedule: { interval: req.body.schedule },
});
res.json({ ok: true });
});
```
{% endtab %}
{% tab title="Python" %}
```python
@app.post("/sellers/<id>/payout-schedule")
def update_schedule(id):
seller = db.sellers.find(id)
allowed = {"daily", "weekly", "monthly"}
if request.json["schedule"] not in allowed:
return {"error": "Invalid schedule"}, 400
evolve.ConnectedAccount.modify(
seller.evolve_account_id,
payout_schedule={"interval": request.json["schedule"]},
)
return {"ok": True}
```
{% endtab %}
{% endtabs %}
You can restrict allowed values — some platforms don't expose `daily` to all sellers because of cash-management overhead.
{% endstep %}
{% step %}
### Set up reserves for new sellers
In **Connect → Settings → Reserves**, configure a rolling reserve for sellers in their first 90 days:
* **Reserve percentage** — usually 5–10%.
* **Reserve window** — usually 90 days rolling.
* **Applies to** — typically "All new sellers in their first 90 days," graduating to no reserve after.
The reserve protects you against early-account fraud and dispute exposure. It rolls off automatically; sellers see their reserved vs payable balance in their portal.
{% hint style="warning" %}
**Communicate reserves clearly during onboarding.** Sellers who discover a reserve mid-month feel ambushed. Mention it in your seller terms, the onboarding email, and the seller's dashboard.
{% endhint %}
{% endstep %}
{% step %}
### Enable on-demand payouts (Enterprise)
For platforms where seller cash flow is the product (gig apps, instant marketplaces), on-demand payouts are differentiating. In **Connect → Settings → Instant payouts**:
* Enable for all sellers, or for a specific tier.
* Decide who pays the 1% fee — seller, platform, or split.
* Set per-day caps if you want.
Surface in the seller's dashboard with a **Pay me now** button. Most platforms cap to 5 instant payouts per day per seller.
{% endstep %}
{% step %}
### Listen for payout events
Subscribe to:
* `payout.created` — payout scheduled. Useful for "your money's on the way" emails.
* `payout.paid` — payout landed in seller's bank.
* `payout.failed` — closed account, frozen account, wrong details.
For failed payouts, your handler should:
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "payout.failed") {
const payout = event.data.object;
await notifySeller(payout.connected_account, "payout_failed", {
reason: payout.failure_reason,
amount: payout.amount,
});
await pauseFurtherPayouts(payout.connected_account);
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "payout.failed":
payout = event.data.object
notify_seller(
payout.connected_account,
"payout_failed",
reason=payout.failure_reason,
amount=payout.amount,
)
pause_further_payouts(payout.connected_account)
```
{% endtab %}
{% endtabs %}
The seller updates their bank account in their portal; your code resumes payouts and the failed amount goes out on the next scheduled payout.
{% endstep %}
{% step %}
### Test the lifecycle
In test mode, use the dashboard's **Test clock** feature in **Connect → Sellers → [seller]**:
* Make a few test charges to build a balance.
* Fast-forward by a day to see a daily payout fire.
* Use the failure-injection toggle to test a failed payout.
Confirm your handler emails the seller correctly and that the dashboard shows the right status.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Sellers see different available balances on different days"</summary>
Available balance fluctuates as new charges land, refunds deduct, and reserves roll off. The seller's portal shows the balance with all three layers explained — make sure your own UI doesn't oversimplify and confuse them.
</details>
<details>
<summary>"A payout failed but the seller swears their bank account is fine"</summary>
Failed payouts include a `failure_reason` from the bank. The most common: account-name mismatch (the seller's name on file with us doesn't match the name on the bank account). The seller fixes this by re-verifying in the portal. Re-trigger the payout once the new bank account is verified.
</details>
<details>
<summary>"On-demand payouts work but cost us money — sellers pay the 1% fee but customers complain about the deduction"</summary>
Common when the fee structure isn't clear. Two fixes: (1) only show the **Pay me now** button when the seller has a balance large enough to make the 1% fee insignificant; (2) absorb the fee yourself for premium-tier sellers as a perk.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Split payments</strong></td><td>What ends up in the seller's balance.</td><td><a href="split-payments.md">split-payments.md</a></td></tr><tr><td><h3><i class="fa-gavel" style="color:$primary;">:gavel:</i></h3></td><td><strong>Disputes at scale</strong></td><td>How disputes affect payouts.</td><td><a href="disputes-at-scale.md">disputes-at-scale.md</a></td></tr><tr><td><h3><i class="fa-building-columns" style="color:$primary;">:building-columns:</i></h3></td><td><strong>Bank verification with Plaid</strong></td><td>Up-front verification prevents most payout failures.</td><td><a href="../verification/plaid-bank-verification.md">plaid-bank-verification.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/marketplace/split-payments.md
---
icon: percent
description: Take your platform's cut on every payment — flat percentages, conditional rules, pass-through fees.
---
# Split each payment with application fees
By the end of this tutorial you'll have a payment split system that takes a flat application fee on every transaction, with hooks to layer conditional rules (per-seller tier, per-product category, per-volume) on top. The build takes about 60 minutes.
This is the platform-revenue side of Connect — your take rate is configured at session creation, the math is automatic, and reporting rolls up across all sellers.
{% hint style="info" %}
**Prerequisites.** [Onboard your first sellers](onboard-sellers.md) finished — you need at least one verified connected account to test against. Read [Connect → Splitting payments](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/platform-setup/splitting-payments) for the underlying model.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Set the platform default
In **Connect → Settings → Default split**, set your platform-wide application fee as a percentage of gross. Most platforms start at 5% and adjust. You can also set a flat-fee component (e.g. 5% + $0.30 per transaction).
This is the default; per-payment overrides take precedence.
{% endstep %}
{% step %}
### Override per checkout session
When you create a Checkout session for a payment that should route to a connected account, pass `application_fee_amount`:
{% tabs %}
{% tab title="Node" %}
```js
const session = await evolve.connect.checkoutSessions.create({
amount: 10000, // $100 gross
currency: "usd",
connected_account: seller.evolve_account_id,
application_fee_amount: computeFee(seller, 10000),
success_url: "...",
cancel_url: "...",
});
```
{% endtab %}
{% tab title="Python" %}
```python
session = evolve.connect.CheckoutSession.create(
amount=10000,
currency="usd",
connected_account=seller.evolve_account_id,
application_fee_amount=compute_fee(seller, 10000),
success_url="...",
cancel_url="...",
)
```
{% endtab %}
{% endtabs %}
`compute_fee` is your function — see the next step.
{% endstep %}
{% step %}
### Implement your fee logic
Most platforms outgrow a flat percentage within months. Build the fee function as a single point of truth, then evolve it as your business model matures:
{% tabs %}
{% tab title="Node" %}
```js
function computeFee(seller, grossAmount) {
// Base rate by seller tier
let rate;
switch (seller.tier) {
case "premium": rate = 0.03; break; // 3%
case "standard": rate = 0.05; break; // 5%
case "new": rate = 0.07; break; // 7% for first 90 days
default: rate = 0.05;
}
// Volume discount: 1.5% above $10k per transaction
if (grossAmount > 10_00000) rate = Math.min(rate, 0.015);
// Promotional 0% on Black Friday weekend
const today = new Date();
if (isBlackFridayWeekend(today)) rate = 0;
return Math.round(grossAmount * rate);
}
```
{% endtab %}
{% tab title="Python" %}
```python
def compute_fee(seller, gross_amount):
rates = {"premium": 0.03, "standard": 0.05, "new": 0.07}
rate = rates.get(seller.tier, 0.05)
if gross_amount > 10_00000:
rate = min(rate, 0.015)
if is_black_friday_weekend(date.today()):
rate = 0
return round(gross_amount * rate)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Decide who eats the processing fee
By default, the card processing fee comes off the platform's application fee — meaning your effective margin is `application_fee - processing_fee`.
To pass the processing fee through to the seller instead:
{% tabs %}
{% tab title="Node" %}
```js
const session = await evolve.connect.checkoutSessions.create({
amount: 10000,
currency: "usd",
connected_account: seller.evolve_account_id,
application_fee_amount: 500,
application_fee_includes_processing: false, // seller absorbs processing
success_url: "...",
cancel_url: "...",
});
```
{% endtab %}
{% tab title="Python" %}
```python
session = evolve.connect.CheckoutSession.create(
amount=10000,
currency="usd",
connected_account=seller.evolve_account_id,
application_fee_amount=500,
application_fee_includes_processing=False,
success_url="...",
cancel_url="...",
)
```
{% endtab %}
{% endtabs %}
Marketplaces with thin take-rates almost always pass through. Marketplaces competing on seller experience absorb. Pick once at the platform level; keep it consistent.
{% endstep %}
{% step %}
### Watch the splits in the dashboard
In **Reports → Application fees**, you'll see daily/weekly/monthly aggregates of your platform revenue, broken down by seller and by date. Charts you'll watch:
* Total application fees per day.
* Take rate (application fees / gross volume).
* Top sellers by gross volume.
* Top sellers by application fees earned.
For deeper analysis (per-product-category, per-cohort) export the application-fees report and pivot in your data warehouse.
{% endstep %}
{% step %}
### Test with refunds
Refund a charge in test mode. By default, the application fee is refunded proportionally — full refund takes the full application fee, partial refund takes a proportional amount.
To override:
{% tabs %}
{% tab title="Node" %}
```js
const refund = await evolve.refunds.create({
charge: "ch_3KsM12pL9qXa7",
refund_application_fee: false, // platform keeps the fee
});
```
{% endtab %}
{% tab title="Python" %}
```python
refund = evolve.Refund.create(
charge="ch_3KsM12pL9qXa7",
refund_application_fee=False,
)
```
{% endtab %}
{% endtabs %}
Useful when the seller is at fault for the refund — the platform keeps its cut.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Sellers complain the dashboard doesn't show what fees they paid"</summary>
The platform's application fee shows up on each charge as a separate line item. From the seller's connected-account dashboard, they see the gross, the application fee deducted, and the net. If sellers complain, walk them to the per-charge detail page — the breakdown is right there.
</details>
<details>
<summary>"Math doesn't match between Evolve and our internal records"</summary>
Most-cited cause: rounding differences. Always work in integer cents, never in float dollars. `round(amount * rate)` in your code; double-check on Evolve's side that the same integer math applies. One sub-cent rounding error per transaction adds up over 10,000 transactions.
</details>
<details>
<summary>"How do I do tiered commissions (e.g. 5% on first $1k, 3% above)"</summary>
Compute it in your fee function. Evolve's API takes a single `application_fee_amount` per session — your platform decides what to put there. The dashboard's reports show the actual per-charge fee, which is enough for sellers to verify.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-money-bill-transfer" style="color:$primary;">:money-bill-transfer:</i></h3></td><td><strong>Per-seller payout schedules</strong></td><td>How seller balances become bank deposits.</td><td><a href="payout-schedules.md">payout-schedules.md</a></td></tr><tr><td><h3><i class="fa-gavel" style="color:$primary;">:gavel:</i></h3></td><td><strong>Disputes at scale</strong></td><td>Refund and dispute splits explained.</td><td><a href="disputes-at-scale.md">disputes-at-scale.md</a></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>Custom onboarding</strong></td><td>For platforms with their own brand.</td><td><a href="custom-onboarding.md">custom-onboarding.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/payment-flows/3d-secure.md
---
icon: shield-halved
description: Apply 3-D Secure where it earns its keep — high-value charges, fraud-prone cohorts, EU SCA cases.
---
# Set up 3-D Secure for high-value charges
By the end of this tutorial you'll have a 3-D Secure rule set that requires authentication for the transactions where the liability shift is worth the friction, and skips it where it isn't. The build takes about 30 minutes.
The trade-off is real: 3DS adds 5–15 seconds and abandons 1–3% of customers. But on a $1,000 charge with a 50% chance of dispute, the math usually favors requiring it.
{% hint style="info" %}
**Prerequisites.** A working checkout integration ([Accept a one-time payment](accept-one-time-payment.md) is enough). Reading [Payments → 3-D Secure and SCA](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/accept-payments/3d-secure) first will help — this tutorial assumes the conceptual model.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Decide on a strictness preset
In **Settings → Risk → 3-D Secure**, three presets to start from:
* **Required only** (default) — Evolve handles required cases (EEA SCA), everything else skips. Use this if you don't have a strong opinion yet.
* **By rule** — your custom rules layered on top of required cases. Use this for the rest of this tutorial.
* **Always** — every payment goes through 3DS. Use only if you're a regulated vertical that needs maximum liability shift.
Pick **By rule**.
{% endstep %}
{% step %}
### Add a high-value rule
Click **Add rule**. Set the condition: `amount > 500 USD`. Set the action: **Require 3DS**. Save.
This is the most-common starting rule. The threshold to pick depends on your average ticket size — somewhere between 2x and 5x your average is a good zone.
{% endstep %}
{% step %}
### Add a new-customer rule
Click **Add rule** again. Condition: `customer.history.successful_payments == 0`. Action: **Require 3DS**.
Customers with no prior successful payments are higher-risk. Combined with the high-value rule, this catches most of the disputes worth catching.
{% endstep %}
{% step %}
### Test in your checkout
Use these test cards in test mode:
| Card | Result |
| --- | --- |
| `4000 0000 0000 0002` | Approved without 3DS challenge |
| `4000 0027 6000 3184` | Approved after 3DS challenge |
| `4000 0082 6000 3178` | Failed 3DS authentication |
For a $501 charge or a new customer, the second card should now show a 3DS challenge step. Walk through it and confirm the charge succeeds.
{% endstep %}
{% step %}
### Watch it in production
After deploying to live mode, the **Routing report** under **Reports** shows a `3ds_required` column. Monitor for the first week:
* What percentage of charges hit 3DS? Should match your rules.
* What's the abandonment rate at the 3DS step? Should be 1–3%.
* What's your dispute rate vs the previous month? Should drop noticeably for the cohort matching your rules.
If abandonment is much higher than 3%, your rule might be too aggressive — relax the threshold.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"3DS triggers even on existing recurring subscriptions"</summary>
Recurring charges with a recorded mandate should be exempt from SCA via the merchant-initiated transaction (MIT) exemption. If your subscriptions are getting 3DS-challenged on renewal, check that you're saving the mandate properly — see [Save cards for repeat customers](save-cards.md).
</details>
<details>
<summary>"Customer says the 3DS challenge is on a Russian/Chinese page they don't read"</summary>
The 3DS UI is provided by the issuer, not Evolve. Evolve passes the customer's IP locale; the issuer decides language. Most banks support 5–10 languages but not all. If your cohort hits this often, mention it in your checkout copy: "Your bank may ask you to confirm — follow their prompts in your bank's app."
</details>
<details>
<summary>"My dispute rate didn't drop after enabling 3DS"</summary>
Two likely causes: (1) your rules don't cover the disputes you're losing — pull a dispute report and see which charges are losing, then check whether your rule would have caught them; (2) the disputes are not fraud — `product_not_received` and `defective` aren't covered by 3DS's liability shift. See [Prevent chargebacks](chargeback-prevention.md) for the non-fraud side.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Prevent chargebacks</strong></td><td>The non-fraud side of dispute reduction.</td><td><a href="chargeback-prevention.md">chargeback-prevention.md</a></td></tr><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><strong>Save cards for repeat customers</strong></td><td>Mandates and merchant-initiated transactions.</td><td><a href="save-cards.md">save-cards.md</a></td></tr><tr><td><h3><i class="fa-arrows-rotate" style="color:$primary;">:arrows-rotate:</i></h3></td><td><strong>Subscription billing</strong></td><td>Where 3DS exemptions especially matter.</td><td><a href="subscription-billing.md">subscription-billing.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/payment-flows/accept-one-time-payment.md
---
icon: cart-shopping
description: Build a working checkout that takes a real test payment in under an hour.
---
# Accept a one-time payment with Checkout
By the end of this tutorial you'll have a working checkout flow — a buyer clicks **Pay**, a hosted Evolve page collects card details, a real test payment lands in your dashboard, and your server gets a webhook event. The whole build takes about 45 minutes.
This is the right starting point if you've never integrated payments before, or if you're prototyping a new flow.
{% hint style="info" %}
**Prerequisites.** A test API key from [dashboard.test.evolve.com](https://gitbook.com), a server that can accept HTTPS requests, and either Node or Python on your machine. If you don't have a server yet, [ngrok](https://ngrok.com) plus a tiny Express app is fine.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Install the SDK
Pick your language:
{% tabs %}
{% tab title="Node" %}
```bash
npm install @evolve/node express
```
{% endtab %}
{% tab title="Python" %}
```bash
pip install evolve flask
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Create a Checkout session on your server
When the buyer clicks **Pay** on your site, your server creates a Checkout session and returns the URL.
{% tabs %}
{% tab title="Node" %}
```js
import Evolve from "@evolve/node";
import express from "express";
const evolve = new Evolve(process.env.EVOLVE_SECRET_KEY);
const app = express();
app.post("/checkout", async (req, res) => {
const session = await evolve.checkoutSessions.create({
amount: 4200,
currency: "usd",
description: "Order #1042",
success_url: "https://yourapp.com/thanks?session={CHECKOUT_SESSION_ID}",
cancel_url: "https://yourapp.com/cart",
});
res.json({ url: session.url });
});
```
{% endtab %}
{% tab title="Python" %}
```python
import os
import evolve
from flask import Flask, jsonify
evolve.api_key = os.environ["EVOLVE_SECRET_KEY"]
app = Flask(__name__)
@app.post("/checkout")
def checkout():
session = evolve.CheckoutSession.create(
amount=4200,
currency="usd",
description="Order #1042",
success_url="https://yourapp.com/thanks?session={CHECKOUT_SESSION_ID}",
cancel_url="https://yourapp.com/cart",
)
return jsonify(url=session.url)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Redirect the buyer
In your front-end, send the buyer to `session.url`:
```html
<button id="pay">Pay $42.00</button>
<script>
document.getElementById("pay").addEventListener("click", async () => {
const res = await fetch("/checkout", { method: "POST" });
const { url } = await res.json();
window.location.href = url;
});
</script>
```
The buyer lands on Evolve's hosted checkout, enters card details, and pays. Use test card `4242 4242 4242 4242` with any future expiry and any CVC.
{% endstep %}
{% step %}
### Add a webhook endpoint
Most production flows react to a webhook rather than relying on the redirect alone (buyers close tabs). Add an endpoint at **Developers → Webhooks → Add endpoint**, subscribe to `charge.succeeded`, and have your server handle it:
{% tabs %}
{% tab title="Node" %}
```js
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
let event;
try {
event = evolve.webhooks.constructEvent(
req.body,
req.headers["evolve-signature"],
process.env.EVOLVE_WEBHOOK_SECRET
);
} catch (err) {
return res.status(400).send("Bad signature");
}
if (event.type === "charge.succeeded") {
fulfillOrder(event.data.object);
}
res.json({ received: true });
});
```
{% endtab %}
{% tab title="Python" %}
```python
from flask import request
@app.post("/webhook")
def webhook():
try:
event = evolve.Webhook.construct_event(
request.get_data(),
request.headers.get("Evolve-Signature"),
os.environ["EVOLVE_WEBHOOK_SECRET"],
)
except evolve.error.SignatureVerificationError:
return "Bad signature", 400
if event.type == "charge.succeeded":
fulfill_order(event.data.object)
return ""
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Verify it end-to-end
Run your server, click **Pay**, complete the test checkout, and confirm:
1. The charge appears in **Payments → All payments** within a few seconds.
2. Your `success_url` fires with the session ID appended.
3. Your webhook handler logs the `charge.succeeded` event.
4. `fulfillOrder` runs once and only once (idempotency check on the event ID — see [Webhooks → Retries and replay](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/retries-and-replay)).
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"My webhook signature verification keeps failing"</summary>
Almost always one of: body parsed before verification (use `express.raw` or equivalent), wrong signing secret (test vs live have different ones), or a proxy that's altering the body. See [Webhooks → Verifying signatures](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks/verifying-signatures).
</details>
<details>
<summary>"Buyers see a generic descriptor on their statement"</summary>
Set your statement descriptor in **Settings → Billing → Statement descriptor**. The default is your registered business name truncated to 22 characters; that's often unrecognizable to customers. Use a brand name they'll recognize.
</details>
<details>
<summary>"The redirect works but I want a custom thank-you page"</summary>
Pass a `success_url` that points at your own page; you can include `{CHECKOUT_SESSION_ID}` as a placeholder and Evolve fills it in. On that page, retrieve the session server-side to confirm the charge succeeded — never trust the redirect alone for fulfillment.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><strong>Save cards for repeat customers</strong></td><td>Charge the same buyer again later.</td><td><a href="save-cards.md">save-cards.md</a></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>Set up 3-D Secure</strong></td><td>Liability shift on high-value charges.</td><td><a href="3d-secure.md">3d-secure.md</a></td></tr><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Prevent chargebacks</strong></td><td>Habits that cut dispute rates.</td><td><a href="chargeback-prevention.md">chargeback-prevention.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/payment-flows/chargeback-prevention.md
---
icon: shield-check
description: A short, practical checklist that cuts dispute rates by 30–50% for most teams.
---
# Build a chargeback-prevention checklist
By the end of this tutorial you'll have a chargeback-prevention plan tuned to your business — not a vague list of best practices, but a specific set of changes you can ship in a day. The build takes about 60 minutes plus engineering time per fix.
This is for teams with a working payments integration that wants to lower dispute rates. If your dispute rate is over 0.5%, this tutorial is the highest-ROI thing you can do.
{% hint style="info" %}
**Prerequisites.** A live Evolve account with at least 30 days of payment history (you need data to find patterns). Read access to your Disputes report.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Pull your last 90 days of disputes
In **Reports → Disputes**, filter to the last 90 days. Export to CSV. The report has reason codes; the distribution across them tells you which prevention to focus on.
For most teams, the distribution looks something like:
| Reason | % of disputes |
| --- | --- |
| `fraudulent` | 40% |
| `unrecognized` | 20% |
| `product_not_received` | 15% |
| `defective` / `not_as_described` | 10% |
| Everything else | 15% |
Your distribution is what matters. Focus the next steps on your top two reasons.
{% endstep %}
{% step %}
### Fix your statement descriptor
The single highest-leverage fix for `unrecognized` disputes. In **Settings → Billing → Statement descriptor**, set it to your most-recognizable brand name within 22 characters. Customers see this on their bank statement weeks later — if it doesn't match what they remember buying, the bank gets a phone call.
If your brand name doesn't fit in 22 chars, add a **dynamic descriptor** per charge that includes the order number:
```
EVOLVE*ACME #1042
```
The base "EVOLVE*ACME" stays consistent; the order number makes the charge searchable in your customer support tooling.
{% endstep %}
{% step %}
### Add a self-serve refund path
The single highest-leverage fix for `fraudulent` disputes that aren't actually fraud. Most "I didn't make this charge" calls to banks are really "I made the charge and want a refund but couldn't figure out how."
* Add a **Get a refund** link to your receipt email.
* Add a **Get a refund** link to the order confirmation page.
* Make the link's destination either auto-refund (for low-value, low-risk products) or a one-click refund request that lands in your support inbox.
{% endstep %}
{% step %}
### Enable 3-D Secure for high-value charges
If `fraudulent` is in your top two and your average ticket is over $200, walk through [Set up 3-D Secure](3d-secure.md). The liability shift means you almost always win these disputes, and the 1–3% checkout-abandonment cost is usually less than the dispute losses.
{% endstep %}
{% step %}
### Wire up dispute alerts
Connect Verifi (Mastercard) and Ethoca (Amex) under **Settings → Risk → Dispute alerts**. These services notify you when a customer initiates a dispute *before* it becomes an official chargeback — usually 24–72 hours of warning.
Use that window to issue a pre-emptive refund. The dispute closes before it counts against your dispute rate or costs the $15 fee.
{% hint style="success" %}
For most marketplaces, dispute alerts pay for themselves in the first month. The $1 per alert is far less than the $15 dispute fee plus the disputed amount you'd have lost.
{% endhint %}
{% endstep %}
{% step %}
### For physical products: send tracking with shipment
Most `product_not_received` disputes are won with tracking and proof of delivery. Wire your fulfillment system to:
1. Send the tracking number to the customer in a shipment email (with a link to the carrier's tracking page).
2. Attach the tracking number to the Evolve charge as metadata:
{% tabs %}
{% tab title="Node" %}
```js
await evolve.charges.update(chargeId, {
metadata: {
tracking_number: "1Z999AA10123456784",
carrier: "ups",
},
});
```
{% endtab %}
{% tab title="Python" %}
```python
evolve.Charge.modify(
charge_id,
metadata={
"tracking_number": "1Z999AA10123456784",
"carrier": "ups",
},
)
```
{% endtab %}
{% endtabs %}
When a `product_not_received` dispute opens, the metadata is auto-included in your evidence response.
{% endstep %}
{% step %}
### Set a dispute-rate threshold alarm
In **Settings → Alerts**, configure an alert when your rolling 30-day dispute rate exceeds 0.75%. The card networks' threshold is 1.0% — getting an alert at 0.75 gives you time to investigate before you hit a monitoring program.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"My dispute rate is fine but rising — what should I look at?"</summary>
Pull a per-product or per-cohort breakdown. A rising dispute rate is usually concentrated in a small slice of your business — a specific SKU with quality issues, a specific traffic source with high fraud, a specific country with weak fraud signals. Once you find it, the fix is targeted (pull the SKU, block the traffic source, require 3DS for the country) rather than systemic.
</details>
<details>
<summary>"We're a marketplace — most disputes are seller-driven"</summary>
The platform-level rate is what the networks watch, regardless of which seller caused them. Two patterns: (1) per-seller dispute monitoring with auto-pause above a threshold, (2) explicit risk pricing — sellers with higher historical dispute rates pay a higher take rate or carry a reserve. See [Handle disputes and refunds at scale](../marketplace/disputes-at-scale.md).
</details>
<details>
<summary>"Customer says they got a refund but the bank dispute came through anyway"</summary>
Sometimes happens — the dispute was already in flight when the refund posted. Submit evidence including the refund receipt; the network sees it and closes in your favor. The refund stays as a refund and the dispute closes "won" without double-deducting.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>3-D Secure</strong></td><td>For the fraud-dispute side.</td><td><a href="3d-secure.md">3d-secure.md</a></td></tr><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><strong>Save cards for repeat customers</strong></td><td>Recognized charges are non-disputed charges.</td><td><a href="save-cards.md">save-cards.md</a></td></tr><tr><td><h3><i class="fa-gavel" style="color:$primary;">:gavel:</i></h3></td><td><strong>Disputes at scale (Connect)</strong></td><td>For marketplace-specific patterns.</td><td><a href="../marketplace/disputes-at-scale.md">disputes-at-scale.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/payment-flows/migrate-from-stripe.md
---
icon: arrows-left-right
description: Cut over from Stripe to Evolve with a parallel-run pattern and zero lost transactions.
---
# Migrate from Stripe
By the end of this tutorial you'll have a cutover plan from Stripe to Evolve that runs both processors in parallel during the migration window, validates payments are landing correctly on the Evolve side, and finally flips the switch with no customer-facing disruption. The migration takes 2–6 weeks of calendar time depending on your scale.
This is for teams with an existing production Stripe integration. If you're greenfield, just use the [accept-a-payment tutorial](accept-one-time-payment.md) directly.
{% hint style="info" %}
**Prerequisites.** A live Stripe account, a live Evolve account (work with your account team to set this up — you'll need an acquirer agreement and underwriting). Read access to your Stripe data export. Engineering bandwidth: about 3 weeks for a small team.
{% endhint %}
{% hint style="warning" %}
**Don't try a hard cutover.** Run both processors in parallel for at least a week. Surprises in production payment systems compound — paralleling gives you a rollback path that doesn't break customer experience.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Map your concepts
Most Stripe concepts map directly. Skim this table before doing anything else:
| Stripe | Evolve | Notes |
| --- | --- | --- |
| `Charge` | `Charge` | Same shape on v2. v3 renames to `Payment`. |
| `PaymentIntent` | `Charge` | Evolve doesn't have a separate PaymentIntent; auth-and-capture is unified. |
| `SetupIntent` | `setup_future_usage` flag on Checkout | Equivalent outcome, simpler API. |
| `Customer` | `Customer` | Same. |
| `PaymentMethod` | `PaymentMethod` | Same. |
| `Subscription` | `Subscription` | Same. Plans use a different schema; see step 4. |
| `Connect` | `Connect` | Account types unified — Evolve has one configurable account model instead of Express/Standard/Custom. |
| `Webhook` | `Webhook` | Same shape. Signing algorithm is HMAC-SHA256 (Stripe also uses this). |
{% endstep %}
{% step %}
### Spin up an Evolve account in test mode
Get test API keys, swap your SDK in a feature branch:
{% tabs %}
{% tab title="Node" %}
```diff
-import Stripe from "stripe";
-const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
+import Evolve from "@evolve/node";
+const evolve = new Evolve(process.env.EVOLVE_SECRET_KEY);
```
{% endtab %}
{% tab title="Python" %}
```diff
-import stripe
-stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
+import evolve
+evolve.api_key = os.environ["EVOLVE_SECRET_KEY"]
```
{% endtab %}
{% endtabs %}
For most teams, this is a sed away. The method names and parameter shapes are largely identical.
{% endstep %}
{% step %}
### Run the test suite
Your existing test suite is gold here. If your tests use `stripe-mock` or VCR-recorded fixtures, you'll need to re-record against Evolve's test mode. Expect 90% of tests to pass with no changes; the 10% that fail are usually webhook-signature differences or specific error code names.
{% endstep %}
{% step %}
### Migrate plans and customers
Two things need to be ported from Stripe to Evolve before the parallel-run window:
* **Subscription plans** — recreate them in Evolve via API or dashboard. Use the same plan IDs (`plan_pro_monthly`, etc.) for clarity.
* **Customer records and saved cards** — Evolve provides a Stripe-import tool that pulls customer records and **network tokens** for saved cards. The actual card numbers don't transfer (PCI), but the tokens do — meaning your customers don't have to re-enter their cards.
The import tool is at **Settings → Migration → Import from Stripe**. It runs against test mode first; double-check the migrated data before going live.
{% endstep %}
{% step %}
### Run both processors in parallel
For your parallel-run window (we recommend 1–2 weeks):
* Route a small percentage of new charges to Evolve (5–10% to start).
* Compare the metrics: approval rate, time-to-auth, dispute rate.
* Existing subscriptions stay on Stripe until you're ready to cut over completely.
Use a feature flag to control the percentage. Easy to roll back if anything looks wrong.
{% endstep %}
{% step %}
### Cut over fully
When metrics on Evolve match or beat Stripe (usually within a week or two), flip the percentage to 100%. Then:
* Migrate the remaining active subscriptions in batch (Settings → Migration → Migrate subscriptions).
* Update your webhook endpoints to point only at the Evolve handler.
* Cancel your Stripe API keys to prevent accidental dual-charging.
* Keep the Stripe account open for 90 days for refunds and dispute responses on charges that were processed there.
{% endstep %}
{% step %}
### Reconcile the cutover month
In the month after cutover, your accounting will see two sets of settlement files — Stripe for the early-month charges and Evolve for the later-month charges. The [Stripe-to-Evolve reconciliation script](https://github.com/GitbookIO/evolve-demo) on GitHub combines them into a unified format your bookkeeper can import.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Webhook signatures aren't matching"</summary>
Stripe and Evolve both use HMAC-SHA256, but the **header format is different** — Stripe uses `Stripe-Signature: t=...,v1=...`, Evolve uses `Evolve-Signature: t=...,v1=...`. Same algorithm, different header name. Update your verifier to read the right header.
</details>
<details>
<summary>"Some saved cards didn't migrate"</summary>
Network tokens migrate; non-tokenized cards don't. About 5–10% of cards on a typical account are non-tokenized — usually older saved methods or smaller issuers. For those, the next charge attempt will fail with `payment_method_required` and your dunning flow should ask the customer to re-enter their card.
</details>
<details>
<summary>"My dispute rate jumped right after cutover"</summary>
Two non-obvious causes: (1) the statement descriptor changed from "ACME-CO" to "EVOLVE*ACME-CO" and customers don't recognize it — fix by setting your descriptor explicitly; (2) Stripe's dispute responses for old charges still flow through Stripe — make sure someone's still watching that queue for 90 days.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-cart-shopping" style="color:$primary;">:cart-shopping:</i></h3></td><td><strong>Accept a one-time payment</strong></td><td>Once you're on Evolve, the canonical checkout build.</td><td><a href="accept-one-time-payment.md">accept-one-time-payment.md</a></td></tr><tr><td><h3><i class="fa-arrows-rotate" style="color:$primary;">:arrows-rotate:</i></h3></td><td><strong>Subscription billing</strong></td><td>Migrating Stripe Subscriptions specifically.</td><td><a href="subscription-billing.md">subscription-billing.md</a></td></tr><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Prevent chargebacks</strong></td><td>The descriptor change is a known landmine.</td><td><a href="chargeback-prevention.md">chargeback-prevention.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/payment-flows/save-cards.md
---
icon: bookmark
description: Save customer cards for one-tap checkout, recurring billing, or unscheduled re-charges.
---
# Save cards for repeat customers
By the end of this tutorial you'll have a flow that saves a customer's card on first checkout, charges it again later without re-prompting, and handles card expiry through account updater. The build takes about 60 minutes.
This is the foundation for subscriptions, repeat-purchase flows, marketplaces, and "buy with one tap" UX.
{% hint style="info" %}
**Prerequisites.** [Accept a one-time payment](accept-one-time-payment.md) finished. Familiarity with the [Saved payment methods](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/accept-payments/saved-payment-methods) concept page helps.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Capture customer consent at first checkout
Show a checkbox or clear text on your checkout — "Save this card for future purchases" — that the customer must opt into. The card networks require explicit consent at the time of first payment.
For a typical e-commerce flow, the wording is something like:
> ☐ Save my card for next time
For a subscription flow, the wording is more directly mandate-like:
> By clicking Subscribe, I authorize Acme Corp to charge my card $19/month until I cancel.
{% endstep %}
{% step %}
### Pass `setup_future_usage` on the Checkout session
When the customer checks the consent box, set `setup_future_usage` on the session you create:
{% tabs %}
{% tab title="Node" %}
```js
const session = await evolve.checkoutSessions.create({
amount: 4200,
currency: "usd",
customer_email: req.body.email,
setup_future_usage: req.body.saveCard ? "off_session" : null,
success_url: "...",
cancel_url: "...",
});
```
{% endtab %}
{% tab title="Python" %}
```python
session = evolve.CheckoutSession.create(
amount=4200,
currency="usd",
customer_email=request.json["email"],
setup_future_usage="off_session" if request.json["save_card"] else None,
success_url="...",
cancel_url="...",
)
```
{% endtab %}
{% endtabs %}
`off_session` means you intend to charge the customer when they're not actively present (subscriptions, automatic re-orders). Use `on_session` for one-tap checkouts where the customer is on your site.
{% endstep %}
{% step %}
### Find the customer's saved methods
After the first successful payment, the saved card is attached to the customer record. List it on the customer:
{% tabs %}
{% tab title="Node" %}
```js
const methods = await evolve.paymentMethods.list({
customer: "cus_4n2P3qR5sT6uV",
type: "card",
});
console.log(methods.data[0].card.last4); // e.g. "4242"
```
{% endtab %}
{% tab title="Python" %}
```python
methods = evolve.PaymentMethod.list(
customer="cus_4n2P3qR5sT6uV",
type="card",
)
print(methods.data[0].card.last4)
```
{% endtab %}
{% endtabs %}
Display this on your customer's profile or order page as "Visa ending in 4242".
{% endstep %}
{% step %}
### Charge the saved method
For a follow-on charge, reference the customer and the saved method:
{% tabs %}
{% tab title="Node" %}
```js
const charge = await evolve.charges.create({
amount: 1500,
currency: "usd",
customer: "cus_4n2P3qR5sT6uV",
payment_method: "pm_3K2pL9qXa7",
off_session: true,
description: "Tipping for order #1042",
});
```
{% endtab %}
{% tab title="Python" %}
```python
charge = evolve.Charge.create(
amount=1500,
currency="usd",
customer="cus_4n2P3qR5sT6uV",
payment_method="pm_3K2pL9qXa7",
off_session=True,
description="Tipping for order #1042",
)
```
{% endtab %}
{% endtabs %}
`off_session: true` tells the issuer the customer isn't physically present, which matters for the SCA exemption decision.
{% endstep %}
{% step %}
### Turn on account updater
Cards expire. To minimize involuntary churn, enable account updater in **Settings → Cards → Account updater**. When the customer's bank reissues their card, the updated details are pulled in automatically — usually a week or two before the old expiry.
This is on by default for Growth and Enterprise. On Starter you have to enable it explicitly.
{% endstep %}
{% step %}
### Email customers before card expiry
Account updater isn't 100%. Belt-and-braces: email customers 30 days before card expiry asking them to update. Use the dashboard's pre-built template under **Customers → Email templates → Card expiry**.
The template links to your Customer Portal where they can update without you writing UI.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"My recurring charges keep failing with `authentication_required`"</summary>
Without a mandate, the issuer treats every recurring charge as a fresh customer-initiated transaction and may demand SCA. Make sure you're recording the mandate at first checkout (the `setup_future_usage` flag handles this when the consent UI is correctly worded).
</details>
<details>
<summary>"I want to charge a saved card from a different brand than the original"</summary>
You can't — the saved method is tied to the original card. If a customer wants to switch from Visa to Amex, they re-enter the new card via Customer Portal. The old card stays on file unless they remove it.
</details>
<details>
<summary>"Customers complain they can't see what cards I have on file"</summary>
Show the saved methods on the customer's profile and on every receipt. Customers who don't know what's saved sometimes "lose" cards and start their bank's chargeback flow when they see an unfamiliar charge.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-arrows-rotate" style="color:$primary;">:arrows-rotate:</i></h3></td><td><strong>Subscription billing</strong></td><td>Saved methods + a recurring schedule.</td><td><a href="subscription-billing.md">subscription-billing.md</a></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>3-D Secure</strong></td><td>How saved-method MIT exemptions work.</td><td><a href="3d-secure.md">3d-secure.md</a></td></tr><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Prevent chargebacks</strong></td><td>Stop "lost card" disputes before they start.</td><td><a href="chargeback-prevention.md">chargeback-prevention.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/payment-flows/subscription-billing.md
---
icon: arrows-rotate
description: Build recurring billing — mandates, automatic retries, dunning emails, and cancellation.
---
# Build a subscription billing system
By the end of this tutorial you'll have a subscription system that signs customers up, charges them on a schedule, retries failed payments, and emails them before card expiry. The build takes about 90 minutes.
This is for SaaS products, membership sites, and anything else where you charge the same customer on a cadence.
{% hint style="info" %}
**Prerequisites.** You've completed [Accept a one-time payment](accept-one-time-payment.md). You'll reuse the Checkout session and webhook setup from there. A test API key with subscription support enabled (it is on Growth and Enterprise; on Starter you'll see an upsell prompt).
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Define a subscription plan in the dashboard
In **Subscriptions → Plans**, create a plan: name, currency, amount, interval (`month` or `year`), and trial length if any. Plans are reusable across customers — most teams have 3–5 plans, not one per customer.
{% endstep %}
{% step %}
### Create a Checkout session in subscription mode
When the customer signs up, create a Checkout session with `mode: "subscription"` and the plan ID:
{% tabs %}
{% tab title="Node" %}
```js
const session = await evolve.checkoutSessions.create({
mode: "subscription",
plan: "plan_pro_monthly",
customer_email: req.body.email,
success_url: "https://yourapp.com/welcome?session={CHECKOUT_SESSION_ID}",
cancel_url: "https://yourapp.com/pricing",
});
```
{% endtab %}
{% tab title="Python" %}
```python
session = evolve.CheckoutSession.create(
mode="subscription",
plan="plan_pro_monthly",
customer_email=request.json["email"],
success_url="https://yourapp.com/welcome?session={CHECKOUT_SESSION_ID}",
cancel_url="https://yourapp.com/pricing",
)
```
{% endtab %}
{% endtabs %}
The customer enters card details once. Evolve creates a **mandate** (their consent to recurring charges) and saves the card.
{% endstep %}
{% step %}
### Handle the subscription webhook events
Subscribe to four events on your webhook endpoint:
* `subscription.created` — first charge succeeded; provision access.
* `subscription.invoice_paid` — recurring charge succeeded; nothing to do.
* `subscription.invoice_failed` — charge failed; we'll retry. See next step.
* `subscription.canceled` — customer or you canceled; revoke access.
{% endstep %}
{% step %}
### Configure retry behavior
In **Subscriptions → Retry policy**, set the retry schedule for failed charges. The default is sensible — retry 3 times over 7 days, then cancel. Custom-tune if your cohort has known seasonal cash-flow patterns.
For each retry attempt, Evolve sends a `subscription.invoice_failed` event. You can email the customer with a "update your card" link that opens a Customer Portal session.
{% endstep %}
{% step %}
### Build a Customer Portal link
Don't build your own card-update UI. Use the hosted Customer Portal:
{% tabs %}
{% tab title="Node" %}
```js
app.post("/portal", async (req, res) => {
const session = await evolve.customerPortalSessions.create({
customer: req.user.evolveCustomerId,
return_url: "https://yourapp.com/account",
});
res.redirect(session.url);
});
```
{% endtab %}
{% tab title="Python" %}
```python
@app.post("/portal")
def portal():
session = evolve.CustomerPortalSession.create(
customer=current_user.evolve_customer_id,
return_url="https://yourapp.com/account",
)
return redirect(session.url)
```
{% endtab %}
{% endtabs %}
The customer can update their card, change plans, view invoices, and cancel — all without you writing the UI.
{% endstep %}
{% step %}
### Test the lifecycle
In test mode, use the timer feature in **Subscriptions → Test clock** to fast-forward through a year of billing in five minutes. Watch invoices generate, see a retry succeed, see a cancellation flow.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Customers don't know they signed up for recurring billing"</summary>
Show the recurring amount and cadence on your sign-up form, on the Checkout, and on the receipt. The card networks require this for mandate validity, and a clearly-disclosed mandate dramatically lowers your dispute rate.
</details>
<details>
<summary>"Retries fire while the customer is already updating their card"</summary>
When you receive `subscription.invoice_failed`, send the customer to the Customer Portal and pause retries for 24 hours via `evolve.subscriptions.pauseRetries(id)`. The customer updates their card, the subscription un-pauses, and the next attempt uses the new card.
</details>
<details>
<summary>"Account updater missed a re-issued card"</summary>
Account updater works for most major US issuers but isn't 100%. For high-value subscriptions, also email customers 30 days before card expiry asking them to update — see the **Subscriptions → Email templates** for the pre-built one.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><strong>Save cards for repeat customers</strong></td><td>The mandate model under the hood.</td><td><a href="save-cards.md">save-cards.md</a></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>3-D Secure</strong></td><td>When required even on subscriptions.</td><td><a href="3d-secure.md">3d-secure.md</a></td></tr><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Prevent chargebacks</strong></td><td>Most subscription disputes are preventable.</td><td><a href="chargeback-prevention.md">chargeback-prevention.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/README.md
---
icon: graduation-cap
description: Step-by-step builds for common Evolve workflows.
cover: .gitbook/assets/tutorials-cover.png
coverY: 0
layout:
width: wide
tableOfContents:
visible: false
---
# Tutorials
{% columns %}
{% column width="50%" %}
Hands-on walkthroughs for the workflows we get the most questions about. Each tutorial follows the same structure — short intro, video walkthrough, stepper with the actual build, common pitfalls, and pointers to what's next.
**Looking for conceptual material?** The product spaces — [Payments](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/), [Identity](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/), [Connect](https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/) — cover the underlying mechanics and dashboard workflows. Tutorials cover end-to-end builds that combine multiple features.
{% endcolumn %}
{% column width="50%" %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
Every tutorial in this section uses the same **content template** — frontmatter, intro, prerequisites hint, video embed, stepper, common-pitfalls expandables, what's-next cards. That consistency is intentional: it makes the structure scannable for repeat readers, and easier for the team to author new ones.
The blocks demoed across these pages: **stepper**, **tabs** (multi-language code samples), **expandable**, **embed** (the YouTube video), **hint**, and **cards**.
{% endhint %}
{% endcolumn %}
{% endcolumns %}
## Build common payment flows
Practical end-to-end builds for the things every payments team needs early.
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-cart-shopping" style="color:$primary;">:cart-shopping:</i></h3></td><td><strong>Accept a one-time payment with Checkout</strong></td><td>From zero to a real charge in under an hour.</td><td><a href="payment-flows/accept-one-time-payment.md">accept-one-time-payment.md</a></td></tr><tr><td><h3><i class="fa-arrows-rotate" style="color:$primary;">:arrows-rotate:</i></h3></td><td><strong>Build a subscription billing system</strong></td><td>Recurring billing with mandates, retries, dunning.</td><td><a href="payment-flows/subscription-billing.md">subscription-billing.md</a></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>Set up 3-D Secure for high-value charges</strong></td><td>Liability shift on the transactions where it matters most.</td><td><a href="payment-flows/3d-secure.md">3d-secure.md</a></td></tr><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><strong>Save cards for repeat customers</strong></td><td>Network tokens, mandates, account updater.</td><td><a href="payment-flows/save-cards.md">save-cards.md</a></td></tr><tr><td><h3><i class="fa-arrows-left-right" style="color:$primary;">:arrows-left-right:</i></h3></td><td><strong>Migrate from Stripe</strong></td><td>Field-by-field cutover plan and parallel-run pattern.</td><td><a href="payment-flows/migrate-from-stripe.md">migrate-from-stripe.md</a></td></tr><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Build a chargeback-prevention checklist</strong></td><td>The handful of habits that cut dispute rates by half.</td><td><a href="payment-flows/chargeback-prevention.md">chargeback-prevention.md</a></td></tr></tbody></table>
## Verify customers and businesses
End-to-end Identity flows tied to product use cases.
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><strong>Add identity verification to sign-up</strong></td><td>Document + selfie wired into your onboarding form.</td><td><a href="verification/identity-on-signup.md">identity-on-signup.md</a></td></tr><tr><td><h3><i class="fa-briefcase" style="color:$primary;">:briefcase:</i></h3></td><td><strong>Verify a business (KYB)</strong></td><td>Beneficial-ownership collection plus sanctions screening.</td><td><a href="verification/kyb.md">kyb.md</a></td></tr><tr><td><h3><i class="fa-building-columns" style="color:$primary;">:building-columns:</i></h3></td><td><strong>Connect a bank account with Plaid</strong></td><td>Instant verification with micro-deposit fallback.</td><td><a href="verification/plaid-bank-verification.md">plaid-bank-verification.md</a></td></tr><tr><td><h3><i class="fa-rotate-right" style="color:$primary;">:rotate-right:</i></h3></td><td><strong>Build a re-verification trigger</strong></td><td>Re-verify on chargebacks, large transactions, and account changes.</td><td><a href="verification/re-verification-trigger.md">re-verification-trigger.md</a></td></tr></tbody></table>
## Run a marketplace with Connect
Platform and marketplace patterns. Heaviest on Connect, with reaches into Identity and Payments.
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-user-plus" style="color:$primary;">:user-plus:</i></h3></td><td><strong>Onboard your first sellers</strong></td><td>Hosted-onboarding-flow walkthrough end to end.</td><td><a href="marketplace/onboard-sellers.md">onboard-sellers.md</a></td></tr><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Split each payment with application fees</strong></td><td>Flat percentages, conditional rules, pass-through fees.</td><td><a href="marketplace/split-payments.md">split-payments.md</a></td></tr><tr><td><h3><i class="fa-money-bill-transfer" style="color:$primary;">:money-bill-transfer:</i></h3></td><td><strong>Configure per-seller payout schedules</strong></td><td>Daily, weekly, monthly, and on-demand.</td><td><a href="marketplace/payout-schedules.md">payout-schedules.md</a></td></tr><tr><td><h3><i class="fa-gavel" style="color:$primary;">:gavel:</i></h3></td><td><strong>Handle disputes and refunds at scale</strong></td><td>Routing per-seller, evidence collection, policy automation.</td><td><a href="marketplace/disputes-at-scale.md">disputes-at-scale.md</a></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>Build a custom Connect onboarding flow</strong></td><td>Programmatic onboarding for teams that want their own UX.</td><td><a href="marketplace/custom-onboarding.md">custom-onboarding.md</a></td></tr></tbody></table>
## Get help
{% columns %}
{% column width="50%" %}
### Talk to support
For account-specific questions, integration help, or production incidents, open a ticket from the dashboard.
<p><a href="https://gitbook.com" class="button primary">Open a ticket</a></p>
{% endcolumn %}
{% column width="50%" %}
### Search the docs
Looking for something specific? The Assistant pulls answers from this site, the API reference, and the community forum.
<p><button type="button" class="button secondary" data-action="search" data-icon="magnifying-glass">Search...</button></p>
{% endcolumn %}
{% endcolumns %}
references/example-site/guides/tutorials/SUMMARY.md
# Table of contents
* [Tutorials](README.md)
## Build common payment flows
* [Accept a one-time payment with Checkout](payment-flows/accept-one-time-payment.md)
* [Build a subscription billing system](payment-flows/subscription-billing.md)
* [Set up 3-D Secure for high-value charges](payment-flows/3d-secure.md)
* [Save cards for repeat customers](payment-flows/save-cards.md)
* [Migrate from Stripe](payment-flows/migrate-from-stripe.md)
* [Build a chargeback-prevention checklist](payment-flows/chargeback-prevention.md)
## Verify customers and businesses
* [Add identity verification to sign-up](verification/identity-on-signup.md)
* [Verify a business (KYB)](verification/kyb.md)
* [Connect a bank account with Plaid](verification/plaid-bank-verification.md)
* [Build a re-verification trigger](verification/re-verification-trigger.md)
## Run a marketplace with Connect
* [Onboard your first sellers](marketplace/onboard-sellers.md)
* [Split each payment with application fees](marketplace/split-payments.md)
* [Configure per-seller payout schedules](marketplace/payout-schedules.md)
* [Handle disputes and refunds at scale](marketplace/disputes-at-scale.md)
* [Build a custom Connect onboarding flow](marketplace/custom-onboarding.md)
references/example-site/guides/tutorials/verification/identity-on-signup.md
---
icon: id-card
description: Add document + selfie verification to your sign-up flow without slowing down conversion.
---
# Add identity verification to sign-up
By the end of this tutorial you'll have a sign-up form that runs identity verification in the background, lets the customer continue using your product immediately, and gates higher-trust actions until verification completes. The build takes about 90 minutes.
This is the right pattern for any product where you need verified identity before high-value actions but don't want to lose customers at the front door.
{% hint style="info" %}
**Prerequisites.** A working sign-up flow in your app. Test API keys for Identity. Read [Identity verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/identity-verification) for the conceptual model.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Decide what's gated by verification
Two patterns most teams use:
* **Soft gate.** Customer can sign up and explore the product. Higher-value actions (withdraw funds, place a large order, post listings publicly) require verification.
* **Hard gate.** Customer can't reach the product without verification. Common for regulated verticals (finance, gambling, age-restricted goods).
Pick soft. It's better for conversion and you can always tighten later. The rest of this tutorial assumes soft.
{% endstep %}
{% step %}
### Create a verification session at sign-up
When the customer hits **Sign up**, your server creates an Identity verification session and stores the session ID on their user record:
{% tabs %}
{% tab title="Node" %}
```js
const customer = await evolve.customers.create({
email: req.body.email,
name: req.body.name,
});
const verification = await evolve.identity.verificationSessions.create({
type: "identity",
customer: customer.id,
return_url: "https://yourapp.com/welcome",
});
await db.users.create({
email: req.body.email,
evolve_customer_id: customer.id,
verification_session_id: verification.id,
verification_status: "pending",
});
res.json({ verifyUrl: verification.url });
```
{% endtab %}
{% tab title="Python" %}
```python
customer = evolve.Customer.create(
email=request.json["email"],
name=request.json["name"],
)
verification = evolve.VerificationSession.create(
type="identity",
customer=customer.id,
return_url="https://yourapp.com/welcome",
)
db.users.create(
email=request.json["email"],
evolve_customer_id=customer.id,
verification_session_id=verification.id,
verification_status="pending",
)
return jsonify(verifyUrl=verification.url)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Surface the verification step to the customer
The customer is in your product immediately. Show a banner or modal:
> Verify your identity to unlock [the high-trust actions]. Takes about a minute.
Link the banner to `verification.url`. Customers who click through complete the verification on Evolve's hosted flow. They come back to `return_url` when done.
You can dismiss the banner per-session (the customer might be on mobile and want to do it later), but keep it visible until verification completes.
{% endstep %}
{% step %}
### Listen for the webhook
Subscribe to the four verification-session events:
* `verification_session.verified` — update the user's status, unlock features.
* `verification_session.failed` — show the customer a retry path or escalate to support.
* `verification_session.manual_review` — leave the user in the "verifying" state; manual review usually resolves within an hour.
* `verification_session.expired` — the customer didn't complete in 24 hours; offer them a fresh link.
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "verification_session.verified") {
await db.users.update(
{ verification_session_id: event.data.object.id },
{ verification_status: "verified" }
);
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "verification_session.verified":
db.users.update_where(
verification_session_id=event.data.object.id,
values={"verification_status": "verified"},
)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Gate the high-trust action
In your product, before letting the user perform a gated action, check their verification status:
```js
if (user.verification_status !== "verified") {
return showVerificationRequiredModal();
}
```
The modal should re-link to the verification URL (or generate a fresh one if expired) and explain why the action requires verification. Customers who understand the *why* abandon less.
{% endstep %}
{% step %}
### Test the failure paths
In test mode, use these fixtures to test each path:
| Document fixture | Result |
| --- | --- |
| `test-dl-front.jpg` | Verified |
| `test-dl-expired.jpg` | Failed — `document_expired` |
| `test-dl-tampered.jpg` | Failed — `document_tampered` |
| `test-dl-unrecognized.jpg` | Manual review |
Walk through each one. Confirm your UI handles all four end-states correctly (success, retry, manual review, expired).
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Customers complete verification but our DB doesn't update"</summary>
Webhook handler bug almost always — most often forgetting to `await` the DB update or returning a non-2xx response. If your webhook returns anything other than 2xx, Evolve retries up to 10 times over 3 days, which can produce duplicate updates if the original eventually succeeded.
</details>
<details>
<summary>"Customers say the camera flow is broken on Safari"</summary>
iOS Safari requires HTTPS for camera access (no `http://` or `localhost` over an unsecured network). If you're testing locally, either tunnel via ngrok or use the dashboard's QR-code share feature to test on a phone connected to a real domain.
</details>
<details>
<summary>"How do I show the customer's verified name and DOB in our UI?"</summary>
After verification succeeds, retrieve the session and read the extracted fields. They're not on the webhook payload by default (privacy default). Show them in your UI with care — accidentally surfacing PII to other users is a leakage class our customers ask about most.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rotate-right" style="color:$primary;">:rotate-right:</i></h3></td><td><strong>Re-verification trigger</strong></td><td>When and how to re-run verification later.</td><td><a href="re-verification-trigger.md">re-verification-trigger.md</a></td></tr><tr><td><h3><i class="fa-building-columns" style="color:$primary;">:building-columns:</i></h3></td><td><strong>Bank verification with Plaid</strong></td><td>The "I'll take ACH from this customer" recipe.</td><td><a href="plaid-bank-verification.md">plaid-bank-verification.md</a></td></tr><tr><td><h3><i class="fa-briefcase" style="color:$primary;">:briefcase:</i></h3></td><td><strong>Verify a business (KYB)</strong></td><td>The same flow but for company customers.</td><td><a href="kyb.md">kyb.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/verification/kyb.md
---
icon: briefcase
description: Verify a business and its beneficial owners — the KYB flow end to end.
---
# Verify a business (KYB)
By the end of this tutorial you'll have a KYB flow that collects business info, identifies beneficial owners, runs identity verification on each owner, screens the business and owners against sanctions lists, and lands a single clean **Verified** status on your platform. The build takes about 2 hours.
This is for marketplaces onboarding business sellers, B2B platforms paying out to vendors, and any team that needs to verify a corporate entity before processing payments on its behalf.
{% hint style="info" %}
**Prerequisites.** An Evolve Enterprise account (KYB is Enterprise-only). Test API keys. Familiarity with the [Business verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/business) concept.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Create a KYB session
When the business operator starts onboarding, your server creates a KYB verification session and gets back a hosted URL:
{% tabs %}
{% tab title="Node" %}
```js
const session = await evolve.identity.verificationSessions.create({
type: "business",
return_url: "https://yourapp.com/verified",
metadata: {
internal_org_id: "org_42",
},
});
res.json({ verifyUrl: session.url });
```
{% endtab %}
{% tab title="Python" %}
```python
session = evolve.VerificationSession.create(
type="business",
return_url="https://yourapp.com/verified",
metadata={"internal_org_id": "org_42"},
)
return jsonify(verifyUrl=session.url)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Send the operator to the hosted form
The hosted form walks the operator through:
1. Legal name, registration country, EIN/equivalent.
2. Business address.
3. Officers and beneficial owners (anyone ≥25% ownership, plus controllers).
4. For each owner: name, DOB, address, ID.
This part takes the operator 5–10 minutes for a simple LLC. Complex ownership structures (holding companies, trusts) take longer.
{% hint style="info" %}
You can pre-fill any field your platform already knows. Pass values via `prefill: { ... }` on the session create — the operator can still edit, but they don't re-enter what you have.
{% endhint %}
{% endstep %}
{% step %}
### Verify each beneficial owner
The hosted form collects names; identity verification on each owner is automatic. Each owner gets an email with a verification link they complete on their own device — no need to be in the same place as the operator.
While owners are verifying, the session sits in `processing` state. You'll get one webhook per owner verification, and a final `verification_session.verified` (or `failed` / `manual_review`) when all owners are done.
{% endstep %}
{% step %}
### Run sanctions screening
Sanctions screening runs automatically as part of the KYB flow — both on the business entity and each beneficial owner. You don't trigger it separately.
If anything matches, the session goes to `manual_review`. Your compliance team reviews the match in the dashboard's **Identity → Disputes / Manual review** queue and either approves (false positive) or rejects.
{% endstep %}
{% step %}
### Listen for the final webhook
When the overall KYB completes, you get one event with the consolidated decision:
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "verification_session.verified" && event.data.object.type === "business") {
await db.organizations.update(
{ kyb_session_id: event.data.object.id },
{ kyb_status: "verified", verified_at: new Date() }
);
enableHighValueFeatures(event.data.object.metadata.internal_org_id);
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "verification_session.verified" and event.data.object.type == "business":
db.organizations.update_where(
kyb_session_id=event.data.object.id,
values={"kyb_status": "verified", "verified_at": datetime.utcnow()},
)
enable_high_value_features(event.data.object.metadata["internal_org_id"])
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Set up ongoing monitoring
KYB isn't a one-shot check. Sanctions lists update daily and ownership changes. Subscribe to `screening.match_added` and surface a banner in the operator's dashboard when it fires:
> A new sanctions match was added for one of your verified owners. Compliance review in progress.
While the match is reviewed, hold any pending payouts to the business. This is automatic if you've set the right policy in **Settings → Connect → On sanctions match**.
{% endstep %}
{% step %}
### Test with the sanctions fixture
Test mode includes a fixture business named "Test Sanctioned Inc." that triggers a sanctions match. Run a KYB against it to confirm your manual-review queue catches it and your downstream policy (payout hold, alert email, etc.) fires correctly.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"KYB takes 5+ business days for one of our customers"</summary>
Usually means an owner hasn't completed their identity verification. Check the session — it shows per-owner status. The bottleneck is almost always one owner who hasn't clicked their email link. Have your operator follow up directly.
</details>
<details>
<summary>"Sanctions screening flagged someone with a common name"</summary>
False-positive rate on sanctions matches at the medium-confidence threshold is real (think: any John Smith). The dashboard's manual-review tool shows the matched-list entry and the verified person side by side; your compliance reviewer marks false positive with a reason and the verification proceeds. Every override is logged in the audit trail.
</details>
<details>
<summary>"We onboard businesses from countries Evolve hasn't mentioned"</summary>
Coverage is broader than the listed countries — most non-US registries are supported on a case-by-case basis. Talk to your account team before launching in a new country; they'll confirm coverage and pre-emptively flag any region-specific rules.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><strong>Identity verification on sign-up</strong></td><td>The same flow for individual customers.</td><td><a href="identity-on-signup.md">identity-on-signup.md</a></td></tr><tr><td><h3><i class="fa-user-plus" style="color:$primary;">:user-plus:</i></h3></td><td><strong>Onboard your first sellers</strong></td><td>KYB plus the rest of Connect onboarding.</td><td><a href="../marketplace/onboard-sellers.md">onboard-sellers.md</a></td></tr><tr><td><h3><i class="fa-rotate-right" style="color:$primary;">:rotate-right:</i></h3></td><td><strong>Re-verification trigger</strong></td><td>Re-run KYB when ownership changes.</td><td><a href="re-verification-trigger.md">re-verification-trigger.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/verification/plaid-bank-verification.md
---
icon: building-columns
description: Verify a customer's bank account with Plaid instant — with micro-deposits as fallback.
---
# Connect a bank account with Plaid
By the end of this tutorial you'll have a bank-account verification flow that uses Plaid instant for ~70% of customers and falls back to micro-deposits for the rest, with a single user-facing UX. The build takes about 60 minutes.
This is the right pattern any time you need a verified bank account — to debit ACH, to send a payout, or to onboard a marketplace seller.
{% hint style="info" %}
**Prerequisites.** A Growth or Enterprise account (bank verification isn't on Starter). Test API keys. A working customer onboarding flow.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Create a bank verification session
Set `method: "auto"` to enable the Plaid-first-with-fallback pattern:
{% tabs %}
{% tab title="Node" %}
```js
const verification = await evolve.identity.bankVerifications.create({
customer: "cus_4n2P3qR5sT6uV",
method: "auto",
return_url: "https://yourapp.com/bank-verified",
});
res.json({ verifyUrl: verification.url });
```
{% endtab %}
{% tab title="Python" %}
```python
verification = evolve.BankVerification.create(
customer="cus_4n2P3qR5sT6uV",
method="auto",
return_url="https://yourapp.com/bank-verified",
)
return jsonify(verifyUrl=verification.url)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Send the customer to the hosted flow
The customer clicks through to a hosted page that:
1. Shows a list of supported banks (Plaid coverage).
2. The customer either picks their bank (→ Plaid flow) or clicks "My bank isn't here" (→ micro-deposits flow).
3. Plaid customers complete in 30–60 seconds. Micro-deposits customers enter routing + account numbers and come back in 1–2 days to confirm amounts.
{% endstep %}
{% step %}
### Listen for the verified webhook
For Plaid, you get the result within seconds. For micro-deposits, 1–2 business days later (after the deposits land and the customer confirms):
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "bank_verification.verified") {
const v = event.data.object;
await db.bankAccounts.create({
customer_id: v.customer,
method: v.method,
last4: v.account_last4,
routing: v.routing,
bank_name: v.bank_name,
verified_at: new Date(),
});
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "bank_verification.verified":
v = event.data.object
db.bank_accounts.create(
customer_id=v.customer,
method=v.method,
last4=v.account_last4,
routing=v.routing,
bank_name=v.bank_name,
verified_at=datetime.utcnow(),
)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Use the verified account
Once verified, the bank account is attached to the customer. To debit it via ACH:
{% tabs %}
{% tab title="Node" %}
```js
const charge = await evolve.charges.create({
amount: 50000,
currency: "usd",
customer: "cus_4n2P3qR5sT6uV",
payment_method_type: "ach_debit",
description: "Subscription renewal",
});
```
{% endtab %}
{% tab title="Python" %}
```python
charge = evolve.Charge.create(
amount=50000,
currency="usd",
customer="cus_4n2P3qR5sT6uV",
payment_method_type="ach_debit",
description="Subscription renewal",
)
```
{% endtab %}
{% endtabs %}
For Connect payouts, just attach the account to the connected account during onboarding — Evolve handles the rest.
{% endstep %}
{% step %}
### Handle the failure cases
Three failure modes:
* **Plaid auth failed** — customer's online banking creds didn't work. The flow auto-falls back to micro-deposits.
* **Routing/account invalid** — wrong numbers entered. `bank_verification.failed` fires; show the customer a retry path.
* **Customer abandoned** — didn't return for micro-deposits. After 7 days, the verification expires; `bank_verification.expired` fires. Email them a fresh link if they haven't completed.
{% endstep %}
{% step %}
### Test in test mode
Test mode includes:
| Test scenario | How to trigger |
| --- | --- |
| Plaid happy path | Pick "Test Bank" in the institution list, use creds `user_good` / `pass_good`. |
| Plaid auth failure | Pick "Test Bank", use creds `user_good` / `wrong`. Falls back to micro-deposits. |
| Micro-deposits success | Use routing `110000000` and any 9-digit account; the test deposits "land" instantly. |
| Micro-deposits failure | Use routing `123456789` (invalid). |
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Customers complete the flow but the webhook fires hours later for Plaid"</summary>
Plaid is fast for the supported banks but slower (~10–30 minutes) for some smaller institutions. Don't treat the redirect-back as confirmation; rely on the webhook. Show a "verifying…" state in your UI when the customer returns, transitioning to "verified" only when the webhook lands.
</details>
<details>
<summary>"Some customers never see Plaid — they go straight to micro-deposits"</summary>
Plaid's bank list is region-aware. US customers see most major banks; EU/UK customers see a much smaller list. For international customers, micro-deposits or open-banking flows specific to their region are the path. Configure regional methods in **Settings → Identity → Bank verification → Regions**.
</details>
<details>
<summary>"Why did a $1.00 micro-deposit show up on my customer's bank statement before they verified?"</summary>
That's the verification step — those deposits are how we prove the customer owns the account. They're refundable; in test mode they don't move money. In live mode the total cost (deposits + fee) is the per-verification charge.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><strong>Identity verification on sign-up</strong></td><td>Verify the person before debiting their account.</td><td><a href="identity-on-signup.md">identity-on-signup.md</a></td></tr><tr><td><h3><i class="fa-user-plus" style="color:$primary;">:user-plus:</i></h3></td><td><strong>Onboard your first sellers</strong></td><td>For Connect, bank verification is part of onboarding.</td><td><a href="../marketplace/onboard-sellers.md">onboard-sellers.md</a></td></tr><tr><td><h3><i class="fa-arrows-rotate" style="color:$primary;">:arrows-rotate:</i></h3></td><td><strong>Subscription billing</strong></td><td>ACH-backed subscriptions are a common pattern.</td><td><a href="../payment-flows/subscription-billing.md">subscription-billing.md</a></td></tr></tbody></table>
references/example-site/guides/tutorials/verification/re-verification-trigger.md
---
icon: rotate-right
description: Re-verify customers when something changes — chargebacks, large transactions, or address updates.
---
# Build a re-verification trigger
By the end of this tutorial you'll have a re-verification system that automatically triggers a fresh identity check on signals that warrant it — first chargeback, transaction over a threshold, or an address change — without disrupting the customer's day-to-day experience. The build takes about 90 minutes.
This is the right pattern for any team that needs to keep verification fresh past the initial onboarding — high-value commerce, regulated verticals, marketplaces.
{% hint style="info" %}
**Prerequisites.** [Identity verification on sign-up](identity-on-signup.md) finished. A working webhook handler. A way to send transactional emails to customers.
{% endhint %}
{% embed url="https://www.youtube.com/watch?v=55oOB-lsQKY" %}
## Build it
{% stepper %}
{% step %}
### Pick your re-verification triggers
Three common ones, ranked by ROI:
* **First chargeback.** A customer's first dispute is the strongest fraud signal you'll get. Re-verify on it.
* **Transaction over threshold.** Pick an amount that reflects your average ticket size — usually 5x to 10x. Re-verify when a customer crosses it.
* **Material profile change.** Address change to a different country, phone number change, or email change. Re-verify on these.
For most teams, all three together produce a manageable volume of re-verifications without irritating customers.
{% endstep %}
{% step %}
### Subscribe to the trigger events
Add these webhook subscriptions:
* `charge.disputed` — for the first-chargeback trigger.
* `charge.succeeded` — for the threshold trigger (filter on amount).
* `customer.updated` — for the profile-change trigger.
{% endstep %}
{% step %}
### Implement the trigger logic
In your webhook handler:
{% tabs %}
{% tab title="Node" %}
```js
async function handleEvent(event) {
const customerId = event.data.object.customer;
const customer = await db.users.findOne({ evolve_customer_id: customerId });
if (!customer) return;
let shouldReverify = false;
if (event.type === "charge.disputed") {
if (customer.disputes_count === 0) shouldReverify = true;
await db.users.update(customerId, { disputes_count: customer.disputes_count + 1 });
}
if (event.type === "charge.succeeded") {
if (event.data.object.amount > 100_000) shouldReverify = true; // $1,000
}
if (event.type === "customer.updated") {
const oldCountry = customer.address?.country;
const newCountry = event.data.object.address?.country;
if (oldCountry && newCountry && oldCountry !== newCountry) {
shouldReverify = true;
}
}
if (shouldReverify && !customer.reverification_pending) {
await triggerReverification(customer);
}
}
```
{% endtab %}
{% tab title="Python" %}
```python
def handle_event(event):
customer_id = event.data.object.customer
customer = db.users.find_by_evolve_id(customer_id)
if not customer:
return
should_reverify = False
if event.type == "charge.disputed":
if customer.disputes_count == 0:
should_reverify = True
db.users.update(customer_id, disputes_count=customer.disputes_count + 1)
if event.type == "charge.succeeded":
if event.data.object.amount > 100_000:
should_reverify = True
if event.type == "customer.updated":
old_country = (customer.address or {}).get("country")
new_country = (event.data.object.address or {}).get("country")
if old_country and new_country and old_country != new_country:
should_reverify = True
if should_reverify and not customer.reverification_pending:
trigger_reverification(customer)
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Trigger the re-verification
Create a fresh verification session and email the customer with the link:
{% tabs %}
{% tab title="Node" %}
```js
async function triggerReverification(customer) {
const session = await evolve.identity.verificationSessions.create({
type: "identity",
customer: customer.evolve_customer_id,
return_url: `https://yourapp.com/reverified?session={CHECKOUT_SESSION_ID}`,
metadata: { reason: "scheduled_reverification" },
});
await db.users.update(customer.id, {
reverification_pending: true,
reverification_session_id: session.id,
});
await sendEmail(customer.email, "reverification_required", {
verifyUrl: session.url,
});
}
```
{% endtab %}
{% tab title="Python" %}
```python
def trigger_reverification(customer):
session = evolve.VerificationSession.create(
type="identity",
customer=customer.evolve_customer_id,
return_url="https://yourapp.com/reverified",
metadata={"reason": "scheduled_reverification"},
)
db.users.update(
customer.id,
reverification_pending=True,
reverification_session_id=session.id,
)
send_email(customer.email, "reverification_required",
{"verifyUrl": session.url})
```
{% endtab %}
{% endtabs %}
{% endstep %}
{% step %}
### Decide what to gate during re-verification
For most teams, the right policy is: customer can keep using the product for low-value actions, but high-value actions (large transactions, withdrawals) are paused until re-verification completes.
{% tabs %}
{% tab title="Node" %}
```js
function canPerformHighValueAction(user) {
if (user.verification_status !== "verified") return false;
if (user.reverification_pending) return false;
return true;
}
```
{% endtab %}
{% tab title="Python" %}
```python
def can_perform_high_value_action(user):
return user.verification_status == "verified" and not user.reverification_pending
```
{% endtab %}
{% endtabs %}
The gate should explain *why* — customers who don't understand abandon at 3x the rate.
{% endstep %}
{% step %}
### Handle the re-verification result
When re-verification succeeds:
{% tabs %}
{% tab title="Node" %}
```js
if (event.type === "verification_session.verified") {
await db.users.update(
{ reverification_session_id: event.data.object.id },
{ reverification_pending: false, last_verified_at: new Date() }
);
}
```
{% endtab %}
{% tab title="Python" %}
```python
if event.type == "verification_session.verified":
db.users.update_where(
reverification_session_id=event.data.object.id,
values={"reverification_pending": False, "last_verified_at": datetime.utcnow()},
)
```
{% endtab %}
{% endtabs %}
If it fails, escalate — the customer's identity has potentially changed since first verification. Most teams send these to manual review for a human to assess.
{% endstep %}
{% endstepper %}
## Common pitfalls
<details>
<summary>"Customers complain about being asked to verify again"</summary>
Most-cited cause is that the email and the gate don't explain why. The wording that works:
> We re-verify customers periodically to keep your account secure and meet our compliance obligations. Verification takes about a minute and only happens [reason].
Customers who understand it's about their security (not yours) abandon less.
</details>
<details>
<summary>"Re-verifications stack up — the same customer gets multiple"</summary>
Always check `reverification_pending` before triggering. If multiple events fire in a short window, one re-verification covers all of them.
</details>
<details>
<summary>"What about customers I want to verify on a fixed schedule?"</summary>
For regulatory regimes that mandate periodic re-verification (some KYC programs require every 12 months for high-risk customers), use the **scheduled re-verification** feature in the dashboard rather than building it yourself. Settings → Identity → Re-verification schedule.
</details>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><strong>Identity verification on sign-up</strong></td><td>The first verification this is a follow-up to.</td><td><a href="identity-on-signup.md">identity-on-signup.md</a></td></tr><tr><td><h3><i class="fa-shield-check" style="color:$primary;">:shield-check:</i></h3></td><td><strong>Prevent chargebacks</strong></td><td>Re-verification is one of many chargeback levers.</td><td><a href="../payment-flows/chargeback-prevention.md">chargeback-prevention.md</a></td></tr><tr><td><h3><i class="fa-briefcase" style="color:$primary;">:briefcase:</i></h3></td><td><strong>Verify a business (KYB)</strong></td><td>The business-side equivalent.</td><td><a href="kyb.md">kyb.md</a></td></tr></tbody></table>
references/example-site/home/.gitbook/includes/persona-switcher.md
---
title: Persona Switcher
---
{% if !visitor.claims.unsigned.persona %}
Try a persona to see adaptive content in action across the site:
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="seedling">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona %}
<i class="fa-id-card-clip" style="color:$info;">:id-card-clip:</i> You are currently <code class="expression">visitor.claims.unsigned.persona === "prospect" ? "a prospect user exploring the product" : visitor.claims.unsigned.persona === "new" ? "a new user" : visitor.claims.unsigned.persona === "existing" ? "an existing user" : visitor.claims.unsigned.persona === "partner" ? "a partner" : ""</code><code class="expression">visitor.claims.unsigned.plan ? ` on the ${visitor.claims.unsigned.plan.charAt(0).toUpperCase() + visitor.claims.unsigned.plan.slice(1)} plan` : ""</code>. [<mark style="color:$primary;">Reset</mark>](https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=)
{% endif %}
{% if visitor.claims.unsigned.persona === "prospect" %}
<a class="button primary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a class="button primary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a class="button primary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a class="button primary" data-icon="handshake-angle">Partner</a>
{% endif %}
references/example-site/home/.gitbook/vars.yaml
support_email: support@evolve.com
partner_email: partners@evolve.com
references/example-site/home/README.md
---
description: >-
Payments, identity, and platform infrastructure — built for businesses that
scale.
icon: house
cover: .gitbook/assets/home-cover.png
coverY: 0
layout:
width: wide
cover:
visible: true
size: full
title:
visible: true
description:
visible: true
tableOfContents:
visible: false
outline:
visible: false
pagination:
visible: true
metadata:
visible: true
tags:
visible: true
---
# Welcome to Evolve
{% columns %}
{% column width="50%" %}
Take payments, verify customers, and run a marketplace — all on one platform. Evolve is the financial infrastructure for modern businesses, used by thousands of teams from early-stage startups to global enterprises.
<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">Ask the Evolve docs</button>
<button type="button" class="button secondary" data-action="ask" data-query="How do I take my first payment?" data-icon="rocket">First payment</button> <button type="button" class="button secondary" data-action="ask" data-query="How do I verify a customer's identity?" data-icon="id-card">Verification</button> <button type="button" class="button secondary" data-action="ask" data-query="How do I onboard sellers on a marketplace?" data-icon="circles-overlap">Marketplace</button>
{% endcolumn %}
{% column width="50%" %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This site is a demo of GitBook's enterprise features applied to a fictional fintech, **Evolve**. It shows what a real customer-facing docs site looks like end-to-end — adaptive content, OpenAPI variants, the AI Assistant with Connections, change-request workflows, hidden pages with public/authenticated flips, and more.
{% if !visitor.claims.unsigned.persona %}
Try a persona to see adaptive content in action across the site:
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="seedling">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona %}
<i class="fa-id-card-clip" style="color:$info;">:id-card-clip:</i> You are currently <code class="expression">visitor.claims.unsigned.persona === "prospect" ? "a prospect user exploring the product" : visitor.claims.unsigned.persona === "new" ? "a new user" : visitor.claims.unsigned.persona === "existing" ? "an existing user" : visitor.claims.unsigned.persona === "partner" ? "a partner" : ""</code><code class="expression">visitor.claims.unsigned.plan ? " on the " + visitor.claims.unsigned.plan.charAt(0).toUpperCase() + visitor.claims.unsigned.plan.slice(1) + " plan" : ""</code>. [<mark style="color:$primary;">Reset</mark>](https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=)
{% endif %}
{% if visitor.claims.unsigned.persona === "prospect" %}
<a class="button primary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a class="button primary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a class="button primary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=new&plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a class="button primary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% endhint %}
{% endcolumn %}
{% endcolumns %}
{% if visitor.claims.unsigned.persona %}
***
# <i class="fa-sparkle" style="color:$info;">:sparkle:</i> Picked for you
{% endif %}
{% if visitor.claims.unsigned.persona === "prospect" %}
{% hint style="info" icon="store" %}
**Evaluating Evolve?** Use the resources below to get an overview of how to get started.
{% endhint %}
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-wallet" style="color:$primary;">:wallet:</i></h3></td><td><h3><strong>Payment methods</strong></h3></td><td>Which payment methods Evolve supports, and which ones are available on each plan.</td><td><a href="https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/payment-methods">Payment methods</a></td></tr><tr><td><h3><i class="fa-life-ring" style="color:$primary;">:life-ring:</i></h3></td><td><h3><strong>Help Center</strong></h3></td><td>Frequently-asked questions about pricing, going live, and supported countries</td><td><a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/NA4Ikc8fQtsXC5U53xJu/">Troubleshooting</a></td></tr><tr><td><h3><i class="fa-receipt" style="color:$primary;">:receipt:</i></h3></td><td><h3><strong>Fees and pricing</strong></h3></td><td>What each plan costs, what's included, and how fees show up in your settlements.</td><td><a href="https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/fees-and-pricing">Fees and pricing</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
{% hint style="info" icon="hand-wave" %}
**Welcome to Evolve.** Start with these — get to a working test charge fast, then explore the things every new account does first.
{% endhint %}
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><h3><strong>Take your first payment</strong></h3></td><td>Five-minute test charge from the dashboard. No integration required.</td><td><a href="https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/quickstart/accept-your-first-payment">accept-your-first-payment</a></td></tr><tr><td><h3><i class="fa-key" style="color:$primary;">:key:</i></h3></td><td><h3><strong>Developer Quickstart</strong></h3></td><td>The same flow with a real API call, in Node, Python, Go, or Ruby.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/getting-started/quickstart">quickstart</a></td></tr><tr><td><h3><i class="fa-graduation-cap" style="color:$primary;">:graduation-cap:</i></h3></td><td><h3><strong>Tutorials</strong></h3></td><td>15 step-by-step builds for the most-asked-about workflows.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/">tutorials</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
{% hint style="info" icon="arrows-left-right" %}
**Migrating from Stripe?** Most concepts map cleanly. Start with the migration tutorial — most teams complete in 2–6 weeks of calendar time.
{% endhint %}
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-arrows-left-right" style="color:$primary;">:arrows-left-right:</i></h3></td><td><h3><strong>Migrate from Stripe</strong></h3></td><td>Field mapping, parallel-run pattern, and the cutover checklist.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/migrate-from-stripe">migrate-from-stripe</a></td></tr><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><h3><strong>Save cards for repeat customers</strong></h3></td><td>Network tokens, mandates, account updater — and how Stripe Customers map.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/save-cards">save-cards</a></td></tr><tr><td><h3><i class="fa-arrows-rotate" style="color:$primary;">:arrows-rotate:</i></h3></td><td><h3><strong>Subscription billing</strong></h3></td><td>Recurring billing, mandates, retries, dunning — direct port from Stripe Subscriptions.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/subscription-billing">subscription-billing</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
{% hint style="info" icon="handshake" %}
**Welcome back.** Your portal has deal registration, marketing assets, training, and a direct line to your partner success manager.
{% endhint %}
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-handshake-angle" style="color:$primary;">:handshake-angle:</i></h3></td><td><h3><strong>Register a deal</strong></h3></td><td>Protect your commission. Status, protection windows, payout timing.</td><td><a href="https://app.gitbook.com/s/R0VawBV5xcQ4exP2PlWS/deal-registration">deal-registration</a></td></tr><tr><td><h3><i class="fa-bullhorn" style="color:$primary;">:bullhorn:</i></h3></td><td><h3><strong>Marketing resources</strong></h3></td><td>Logos, brand kit, case studies, co-marketing programs.</td><td><a href="https://app.gitbook.com/s/R0VawBV5xcQ4exP2PlWS/marketing-resources">marketing-resources</a></td></tr><tr><td><h3><i class="fa-graduation-cap" style="color:$primary;">:graduation-cap:</i></h3></td><td><h3><strong>Training</strong></h3></td><td>Three certifications, self-paced courses, monthly live sessions.</td><td><a href="https://app.gitbook.com/s/R0VawBV5xcQ4exP2PlWS/training">training</a></td></tr></tbody></table>
{% endif %}
***
## Three products, one platform
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-credit-card" style="color:$primary;">:credit-card:</i></h3></td><td><h3><strong>Payments</strong></h3></td><td>Accept card and bank-rail payments. Smart routing, 3-D Secure, settlement, reporting.</td><td><a href="https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/">payments</a></td></tr><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><h3><strong>Identity</strong></h3></td><td>Verify customers and businesses. Document review, selfie liveness, bank verification, KYB.</td><td><a href="https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/">identity</a></td></tr><tr><td><h3><i class="fa-circles-overlap" style="color:$primary;">:circles-overlap:</i></h3></td><td><h3><strong>Connect</strong></h3></td><td>Embed payments in your platform. Onboard sellers, split payments, run a marketplace.</td><td><a href="https://app.gitbook.com/s/Xtfxb7OHGyrdfIsObmnu/">connect</a></td></tr></tbody></table>
## For developers
API references, SDKs, and the agent integrations across all three products. Pick where to dive in:
{% columns %}
{% column width="25%" %}
<i class="fa-rocket" style="color:$primary;">:rocket:</i> **Quickstart**
Make your first API call in five minutes.
<a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Si95BtOt1VRLWjT7A67V/" class="button primary">Quickstart</a>
{% endcolumn %}
{% column width="25%" %}
<i class="fa-key" style="color:$primary;">:key:</i> **Authentication**
Keys, restricted scopes, signature verification.
<a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Si95BtOt1VRLWjT7A67V/" class="button secondary">Authenticated</a>
{% endcolumn %}
{% column width="25%" %}
<i class="fa-cubes" style="color:$primary;">:cubes:</i> **SDKs**
Node, Python, Go, Ruby — official and idiomatic.
<a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Si95BtOt1VRLWjT7A67V/" class="button secondary">SDKs</a>
{% endcolumn %}
{% column width="25%" %}
<i class="fa-robot" style="color:$primary;">:robot:</i> **AI agents**
llms.txt, MCP server, agent best practices.
<a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Si95BtOt1VRLWjT7A67V/" class="button secondary">For AI agents</a>
{% endcolumn %}
{% endcolumns %}
## Learn and explore
{% columns %}
{% column width="66.66666666666666%" %}
#### <i class="fa-compass" style="color:$primary;">:compass:</i> Guides
Everything you need to get the most out of the Evolve platform.
<details open>
<summary><i class="fa-graduation-cap" style="color:$primary;">:graduation-cap:</i> <strong>Tutorials</strong></summary>
Step-by-step builds for common workflows. 15 tutorials with video walkthroughs.
<a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Nankrp40VchJsUblU6h6/" class="button secondary">Start building</a>
</details>
<details open>
<summary><i class="fa-graduation-cap" style="color:$primary;">:graduation-cap:</i> <strong>Help Center</strong></summary>
Focused answers to common questions. The Assistant pulls from here, the forum, and YouTube.
<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">How can we help?</button><a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/NA4Ikc8fQtsXC5U53xJu/" class="button secondary">View all</a>
</details>
<details open>
<summary><i class="fa-puzzle-piece" style="color:$primary;">:puzzle-piece:</i> <strong>Integration guides</strong></summary>
Slack, Zapier, Segment, QuickBooks, NetSuite — and more in active development.
<a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/MBT3EDUK7DzXmR0k9cje/" class="button secondary">Browse integrations</a>
</details>
{% endcolumn %}
{% column width="33.33333333333334%" %}
### <i class="fa-clock-rotate-left">:clock-rotate-left:</i> What's new
Our biggest recent releases
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-route" style="color:$primary;">:route:</i></h3></td><td><h3><strong>Smart routing v2</strong></h3></td><td>Per-network success-rate optimization. 1–3% lift in approval rate, no code changes.</td><td><a href="#smart-routing-v2">Broken link</a></td></tr><tr><td><h3><i class="fa-flask" style="color:$primary;">:flask:</i></h3></td><td><h3><strong>Payments API v3-beta</strong></h3></td><td>Renamed Payment object, capture_method enum, 30-day auth, multi-currency capture.</td><td><a href="#payments-api-v3-beta-available">Broken link</a></td></tr><tr><td><h3><i class="fa-face-smile" style="color:$primary;">:face-smile:</i></h3></td><td><h3><strong>Selfie liveness 2.0</strong></h3></td><td>Passive liveness — no head turns. Lifts completion rates by ~12%.</td><td><a href="#selfie-liveness-2.0">Broken link</a></td></tr></tbody></table>
<a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ErQsbFsgm6eg9BApdmPl/" class="button secondary" data-icon="clock-rotate-left">View complete changelog</a>
{% endcolumn %}
{% endcolumns %}
***
## Partner with Evolve
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><h3><i class="fa-handshake" style="color:$primary;">:handshake:</i> <strong>Become a partner</strong></h3></td><td>Solution, implementation, and technology partners earn revenue share, get co-marketing support, and a direct line to your partner success manager.</td><td><a href="https://gitbook.com/enterprise" class="button primary">Apply to the program</a> <a href="https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/R0VawBV5xcQ4exP2PlWS/" class="button secondary">Learn more</a></td></tr><tr><td><h3><i class="fa-key" style="color:$primary;">:key:</i> <strong>Already a partner?</strong></h3></td><td>Sign in to the partner portal for deal registration, marketing resources, training, and support.</td><td><a href="https://enterprise-demos.gitbook.io/evolve-docs?visitor.persona=partner&visitor.plan=enterprise" class="button primary">Sign in</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/partners?visitor.persona=partner&visitor.plan=enterprise" class="button secondary">Open the portal</a></td></tr></tbody></table>
## Get help
<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><h3><i class="fa-headset" style="color:$primary;">:headset:</i> <strong>Talk to support</strong></h3></td><td>For account-specific questions, integration help, or production incidents, open a ticket by starting a chat below.</td><td><button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">How can we help?</button></td></tr><tr><td><h3><i class="fa-circle-check" style="color:$primary;">:circle-check:</i> <strong>Platform status</strong></h3></td><td>Real-time status of every Evolve service. Subscribe via email or RSS for incident updates.</td><td><a href="https://gitbook.com" class="button secondary">View status</a></td></tr><tr><td><h3><i class="fa-comments" style="color:$primary;">:comments:</i> <strong>Community</strong></h3></td><td>Real-time discussion with other Evolve customers and our team. Most product questions have a thread.</td><td><a href="https://gitbook.com" class="button secondary">Join the forum</a></td></tr></tbody></table>
references/example-site/home/SUMMARY.md
# Table of contents
* [Welcome to Evolve](README.md)
references/example-site/partners/.gitbook/vars.yaml
api_live: https://api.evolve.com
dashboard_live: https://dashboard.evolve.com
support_email: support@evolve.com
partner_email: partners@evolve.com
partner_slack: https://evolve-partners.slack.com
references/example-site/partners/deal-registration.md
---
icon: handshake-angle
hidden: true
description: Register a deal, track its status, see your protection windows.
---
# Deal registration
{% if visitor.claims.unsigned.persona !== "partner" %}
{% hint style="warning" icon="lock" %}
**This page is for active Evolve partners.** [Sign in to the partner portal](https://gitbook.com) to access deal registration. Not yet a partner? [Apply to the program](README.md).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
Registering deals early protects your commission for the duration of the deal cycle and gives the Evolve team enough context to support you in the sales process.
## Register a new deal
<p><a href="https://gitbook.com" class="button primary">Open registration form</a></p>
The form takes about 3 minutes. You'll need:
* The prospect's company name and primary contact.
* The expected use case — Payments, Identity, Connect, or some combination.
* The deal stage (qualified, proposal, contract).
* Estimated annual transaction volume or verification volume.
## Protection windows
Once a deal is registered and accepted, your protection window depends on the deal stage:
| Stage at registration | Protection window |
| --- | --- |
| **Qualified** | 60 days |
| **Proposal** | 90 days |
| **Contract sent** | 120 days |
If the deal closes within the window, you're the partner of record and earn full commission. After the window expires, the deal returns to the open pool and any partner (including direct sales) can claim it.
## What disqualifies a deal
A handful of conditions prevent registration acceptance:
* The prospect is already an active Evolve customer.
* The prospect is already in another partner's open registration.
* The prospect is in active direct-sales conversation with the Evolve team (we'll let you know within 48 hours of registration).
* The prospect is on the OFAC blocklist or in a vertical Evolve doesn't serve.
If your registration is rejected for any reason, you'll get a notification with the reason within 2 business days.
## Tracking deal status
The dashboard's **Deal status** view shows every deal you've registered with its current stage, days remaining in protection, and last-touch from the Evolve team.
For deals that have stalled, your partner success manager (PSM) can help re-engage. The "Request PSM help" button on each deal opens a Slack thread with your PSM included.
## Commission timing
Commissions are calculated quarterly based on referred-customer activity in the prior quarter. Payment lands in your registered bank account on the 15th of the second month after quarter close (so Q1 commissions pay May 15th).
The dashboard's **Commission preview** shows projected payout based on activity to date.
## Related
* [Marketing resources](marketing-resources.md) — co-marketing assets for joint sales activities.
* [Training](training.md) — sales certification, technical deep-dives.
* [Partner support](support.md) — your PSM contact details and escalation paths.
{% endif %}
references/example-site/partners/marketing-resources.md
---
icon: bullhorn
hidden: true
description: Logos, brand kit, case studies, co-marketing templates.
---
# Marketing resources
{% if visitor.claims.unsigned.persona !== "partner" %}
{% hint style="warning" icon="lock" %}
**This page is for active Evolve partners.** [Sign in to the partner portal](https://gitbook.com) to access marketing resources. Not yet a partner? [Apply to the program](README.md).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
Brand assets, co-marketing templates, and case studies you can use in your own sales and marketing materials.
## Logos and brand kit
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-image" style="color:$primary;">:image:</i></h3></td><td><strong>Logo pack</strong></td><td>SVG, PNG, EPS in light and dark variants. Use guidelines included.</td><td></td></tr><tr><td><h3><i class="fa-palette" style="color:$primary;">:palette:</i></h3></td><td><strong>Brand kit</strong></td><td>Colors, typography, spacing rules. Figma library included.</td><td></td></tr><tr><td><h3><i class="fa-handshake" style="color:$primary;">:handshake:</i></h3></td><td><strong>Co-branded templates</strong></td><td>One-pagers, slide decks, case-study templates. Editable PowerPoint and Keynote.</td><td></td></tr></tbody></table>
<p><a href="https://gitbook.com" class="button primary">Download asset pack</a></p>
## Case studies
Three case studies refreshed for Q2 2026:
| Customer | Vertical | Use case | Outcome |
| --- | --- | --- | --- |
| **Stride Commerce** | Marketplace platform | Connect | 2.3% approval rate lift, 40% lower platform infra cost |
| **Reilly Payments Group** | Implementation partner | Payments | Migrated 23 client accounts in 6 weeks |
| **Northwind SaaS** | Vertical B2B SaaS | Subscription billing + Identity | 60% reduction in churn from involuntary card-decline |
All three include partner-quotable executive endorsements and are available as downloadable PDFs (one-page summaries) and full case-study PDFs.
## Co-marketing programs
Two programs you can opt into per-quarter:
### <i class="fa-newspaper" style="color:$primary;">:newspaper:</i> Joint case study
Work with our marketing team to develop a written and video case study around a successful customer. Promoted across both partner and Evolve channels. Limited to 8 partners per quarter.
### <i class="fa-microphone" style="color:$primary;">:microphone:</i> Conference co-presence
Joint booth space and presentation slots at major industry conferences (Money 20/20, FinTech Connect, Marketplace Risk). Partner co-investment required.
Apply via your PSM or the **Co-marketing** tab in the partner portal.
## Webinar program
Monthly webinars co-hosted by Evolve and partner subject-matter experts. Recorded, distributed to both audiences. Topics rotate quarterly:
* Q2 2026: Marketplace risk management.
* Q3 2026: Identity verification at scale.
* Q4 2026: Subscription dunning best practices.
Sign up to host or co-present via your PSM.
## Lead-sharing program
Active partners with a quarterly minimum referral count get access to **curated lead lists** — Evolve customers who've expressed interest in the partner's vertical or specific implementation expertise. The list is updated monthly.
Lead-sharing is opt-in; sign up via the **Lead sharing** tab in the partner portal.
## Asset usage policy
Some quick rules:
* **Don't modify the Evolve logo** — colors, proportions, lockup are fixed. Use the variants we provide.
* **"Partner" badge** — only Certified Implementer Partners may use the "Certified Evolve Implementer" badge. Other partners use the standard "Evolve Partner" badge.
* **Customer-name use** — case studies and customer logos require the customer's written approval, which we coordinate.
* **Press releases** — coordinate via [partners@evolve.com](mailto:partners@evolve.com) before publishing.
Full guidelines in the brand kit.
## Related
* [Training](training.md) — keep your team's sales and technical skills sharp.
* [Deal registration](deal-registration.md) — protect your commission while you sell.
* [Partner support](support.md) — your PSM and escalation paths.
{% endif %}
references/example-site/partners/README.md
---
icon: handshake
description: Build your business on Evolve. Partner with us to deliver payments, identity, and platform solutions to your customers.
cover: .gitbook/assets/partners-cover.png
coverY: 0
layout:
width: wide
tableOfContents:
visible: false
---
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This space demonstrates the public/authenticated **flip**: anonymous visitors see the partner pitch below; authenticated partners see a portal home in the same place. The flip is driven by adaptive content on `visitor.claims.unsigned.persona`. Once you flip into partner mode, the linked portal pages (deal registration, marketing resources, etc.) become accessible — they're **hidden pages**, not in the public sidebar nav, but reachable via direct link or from the portal home.
{% if visitor.claims.unsigned.persona === "partner" %}
<i class="fa-id-card-clip" style="color:$info;">:id-card-clip:</i> You are currently signed in as a partner<code class="expression">visitor.claims.unsigned.plan ? " on the " + visitor.claims.unsigned.plan.charAt(0).toUpperCase() + visitor.claims.unsigned.plan.slice(1) + " plan" : ""</code>. [<mark style="color:$primary;">Reset</mark>](https://enterprise-demos.gitbook.io/evolve-docs/partners?visitor.persona=)
{% endif %}
{% if visitor.claims.unsigned.persona !== "partner" %}
<a class="button primary" data-icon="globe">Public</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/partners?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Signed-in partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/partners?visitor.persona=" class="button secondary" data-icon="globe">Public</a> <a class="button primary" data-icon="handshake-angle">Signed-in partner</a>
{% endif %}
{% endhint %}
{% if visitor.claims.unsigned.persona !== "partner" %}
# Partner with Evolve
Whether you build software for businesses, implement payments for clients, or operate a marketplace platform, Evolve has a partner program that fits — with revenue share, co-marketing, and dedicated support.
<p><a href="https://gitbook.com" class="button primary">Apply to become a partner</a> <a href="https://gitbook.com" class="button secondary">Sign in to the partner portal</a></p>
## Why partner with Evolve
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Revenue share</strong></td><td>Earn a share of every transaction your referrals process — for the lifetime of the relationship.</td><td></td></tr><tr><td><h3><i class="fa-headset" style="color:$primary;">:headset:</i></h3></td><td><strong>Dedicated support</strong></td><td>Direct line to a partner success manager. Slack channel for technical escalation.</td><td></td></tr><tr><td><h3><i class="fa-bullhorn" style="color:$primary;">:bullhorn:</i></h3></td><td><strong>Co-marketing</strong></td><td>Joint case studies, conference appearances, and curated lead lists for active partners.</td><td></td></tr></tbody></table>
## Partner types
{% columns %}
{% column width="33%" %}
### <i class="fa-cubes" style="color:$primary;">:cubes:</i> Solution Partners
You build software that integrates Evolve — checkout plugins, marketplace platforms, vertical SaaS. Earn revenue share on every customer who uses your integration.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-screwdriver-wrench" style="color:$primary;">:screwdriver-wrench:</i> Implementation Partners
You implement Evolve for clients. Earn referral fees on every client you bring on, plus access to our certified-implementer program for marketing visibility.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-circle-nodes" style="color:$primary;">:circle-nodes:</i> Technology Partners
You build a complementary product (CRM, analytics, fraud detection). Get listed in our [integrations directory](https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/) and access early-look APIs.
{% endcolumn %}
{% endcolumns %}
## What partners are saying
{% columns %}
{% column width="50%" %}
> "We integrated Evolve into our marketplace platform last year. The revenue share covers 40% of our infrastructure costs now — and our customers' approval rates went up 2.3% on average."
>
> **— Maria Chen, CTO, Stride Commerce**
{% endcolumn %}
{% column width="50%" %}
> "We're a regional implementation shop and Evolve's partner program is the most generous we've seen. Their team actually returns calls — that's worth more than the rev share."
>
> **— Devon Reilly, Reilly Payments Group**
{% endcolumn %}
{% endcolumns %}
## How the program works
{% stepper %}
{% step %}
### Apply
Tell us about your business. Most applications get a response within 5 business days; technology partners typically clear faster than implementation partners since the diligence is lighter.
{% endstep %}
{% step %}
### Onboarding
Once approved, you get portal access, an assigned partner success manager, and a 30-minute kickoff call. We walk through portal tools and answer your specific integration or sales questions.
{% endstep %}
{% step %}
### Refer or build
Solution and Technology partners build their integration; Implementation partners start referring clients. Either way, every transaction tracked to you earns revenue share.
{% endstep %}
{% step %}
### Grow
Quarterly reviews with your partner success manager — what's working, what to invest in, what new features matter to your customers.
{% endstep %}
{% endstepper %}
## Frequently asked questions
<details>
<summary>Is there a fee to join the program?</summary>
No. Evolve doesn't charge any application fee or annual membership. Revenue share is paid out on a quarterly basis based on referred-customer activity.
</details>
<details>
<summary>Do I need to be Evolve-certified?</summary>
For Implementation Partners, certification is required to use the "Certified Evolve Implementer" badge — but optional otherwise. Certification is free; it's a 4-hour online course plus a final exam. See the partner portal for details.
</details>
<details>
<summary>What about exclusivity?</summary>
Non-exclusive. Most of our partners work with multiple payment providers; we'd rather earn the right to be your preferred option than contract our way to it.
</details>
<details>
<summary>How is revenue share calculated?</summary>
A percentage of net Evolve revenue from referred customers — typically 10–25% depending on partner tier and customer commitment. Specifics are in your partner agreement.
</details>
## Apply
<p><a href="https://gitbook.com" class="button primary">Start your application</a> <a href="mailto:partners@evolve.com" class="button secondary">Email the partner team</a></p>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
# Partner Portal
Welcome back. Here's what's new and where to find what you need.
<p><a href="deal-registration.md" class="button primary">Register a new deal</a> <a href="https://gitbook.com" class="button secondary">Open partner Slack</a></p>
## Quick links
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-handshake-angle" style="color:$primary;">:handshake-angle:</i></h3></td><td><strong>Deal registration</strong></td><td>Register an opportunity, track deal status, see protection windows.</td><td><a href="deal-registration.md">deal-registration.md</a></td></tr><tr><td><h3><i class="fa-bullhorn" style="color:$primary;">:bullhorn:</i></h3></td><td><strong>Marketing resources</strong></td><td>Logos, brand kit, case studies, co-marketing templates.</td><td><a href="marketing-resources.md">marketing-resources.md</a></td></tr><tr><td><h3><i class="fa-graduation-cap" style="color:$primary;">:graduation-cap:</i></h3></td><td><strong>Training</strong></td><td>Certification programs, sales enablement, technical deep-dives.</td><td><a href="training.md">training.md</a></td></tr><tr><td><h3><i class="fa-headset" style="color:$primary;">:headset:</i></h3></td><td><strong>Partner support</strong></td><td>Direct line to your partner success manager. Escalation paths.</td><td><a href="support.md">support.md</a></td></tr></tbody></table>
## Recent updates
{% updates format="full" %}
{% update date="2026-04-25" %}
## Q2 partner kit refresh
Updated logo guidelines, brand kit, and three new case studies (Stride Commerce, Reilly Payments Group, Northwind SaaS) live in [Marketing resources](marketing-resources.md).
{% endupdate %}
{% update date="2026-04-10" %}
## New deal-registration UI
The deal-registration form is now mobile-friendly, supports multi-stakeholder fields, and shows real-time deal status. [Try it](deal-registration.md).
{% endupdate %}
{% update date="2026-03-22" %}
## Spring partner summit recordings
Recordings from the March summit (keynote, technical sessions, Q&A) are in [Training → Recordings](training.md#recordings).
{% endupdate %}
{% endupdates %}
## Need help?
{% columns %}
{% column width="50%" %}
### <i class="fa-headset" style="color:$primary;">:headset:</i> Your partner success manager
Direct line to the person on our team who knows your account.
<p><a href="support.md" class="button secondary">Contact details</a></p>
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-comments" style="color:$primary;">:comments:</i> Partner Slack
Technical and sales discussion with other Evolve partners and our team.
<p><a href="https://gitbook.com" class="button secondary">Open Slack</a></p>
{% endcolumn %}
{% endcolumns %}
{% endif %}
references/example-site/partners/SUMMARY.md
# Table of contents
* [Partners](README.md)
* [Deal registration](deal-registration.md)
* [Marketing resources](marketing-resources.md)
* [Training](training.md)
* [Partner support](support.md)
references/example-site/partners/support.md
---
icon: headset
hidden: true
description: Direct line to your partner success manager, escalation paths, and partner Slack.
---
# Partner support
{% if visitor.claims.unsigned.persona !== "partner" %}
{% hint style="warning" icon="lock" %}
**This page is for active Evolve partners.** [Sign in to the partner portal](https://gitbook.com) to access partner support contacts. Not yet a partner? [Apply to the program](README.md).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
Partners get direct access to a partner success manager (PSM), a private Slack channel, and a dedicated escalation path for technical and customer issues.
## Your partner success manager
Every active partner is assigned a PSM. They're your single point of contact for:
* Strategic partnership questions.
* Deal registration help and roadmap-influence requests.
* Quarterly business reviews.
* Co-marketing program coordination.
* Customer escalations that need executive attention.
Your PSM's contact details are on the **Account** tab in the partner portal. Most PSMs prefer Slack for day-to-day; email or video call for anything substantial.
## Partner Slack
The Evolve partner Slack workspace has channels for:
* `#general` — partner-to-partner discussion.
* `#technical-help` — engineering escalation, monitored by the Evolve solutions team during business hours.
* `#deal-help` — sales and pricing questions, monitored by partner-success leadership.
* `#announcements` — Evolve product updates aimed at the partner audience.
* Per-partner private channels with your PSM and a few of our team.
<p><a href="https://gitbook.com" class="button primary">Open the workspace</a></p>
## Escalation paths
Three escalation levels, in order:
{% stepper %}
{% step %}
### Your PSM
For most things — partner program questions, deal help, customer escalations, roadmap requests. Slack DM or email; response time is typically within 4 business hours.
{% endstep %}
{% step %}
### Partner team leadership
When your PSM is out or the issue is bigger than a single PSM can handle. Email [partners@evolve.com](mailto:partners@evolve.com) with "ESCALATION" in the subject. Response within 1 business day.
{% endstep %}
{% step %}
### Executive sponsor
For partner-program-level issues that need a higher altitude. Each partner with $1M+ annual referred volume gets an executive sponsor on the Evolve leadership team — your PSM has the contact details.
{% endstep %}
{% endstepper %}
## Customer escalations
When one of your referred customers has a production issue that needs urgent attention:
1. **First**: have the customer open a support ticket via the standard [<code class="expression">space.vars.dashboard_live</code>/support](https://gitbook.com) flow.
2. If the issue is critical (production-impacting, security, etc.), Slack-DM your PSM with the ticket number — they'll route to the right team and get a real-time update for you.
3. For incidents affecting multiple customers, [<code class="expression">space.vars.status_page</code>](https://gitbook.com) is the canonical source.
Don't message your PSM with non-urgent customer questions — those should flow through the standard support channel so they're tracked.
## Office hours
Live drop-in sessions on the second Tuesday of each month, 11am ET / 4pm GMT. Whatever you bring, partner-success leadership and rotating product/engineering folks are there.
Office hours are recorded; recordings live in [Training → Recordings](training.md#recordings).
## SLAs
* **Slack technical-help**: response within 4 business hours.
* **PSM**: response within 4 business hours; resolution depends on the request.
* **Escalation email**: response within 1 business day.
* **Critical customer escalation**: real-time triage; resolution per the customer's contracted SLA.
## Status and incidents
Platform status: [<code class="expression">space.vars.status_page</code>](https://gitbook.com).
For incidents affecting your customers specifically, your PSM will reach out within 30 minutes of incident detection. Subscribe to status-page updates via email or RSS for the firehose.
## Related
* [Deal registration](deal-registration.md) — protect commissions on the deals you're closing.
* [Marketing resources](marketing-resources.md) — assets for joint sales activities.
* [Training](training.md) — certification, recordings, sales enablement.
{% endif %}
references/example-site/partners/training.md
---
icon: graduation-cap
hidden: true
description: Sales enablement, technical deep-dives, and partner certification.
---
# Training
{% if visitor.claims.unsigned.persona !== "partner" %}
{% hint style="warning" icon="lock" %}
**This page is for active Evolve partners.** [Sign in to the partner portal](https://gitbook.com) to access training. Not yet a partner? [Apply to the program](README.md).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
Self-paced courses, live training, and certification programs to keep your team's Evolve skills sharp.
## Certification programs
Three certifications, free for active partners:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-certificate" style="color:$primary;">:certificate:</i></h3></td><td><strong>Certified Evolve Implementer</strong></td><td>For implementation partners. 4-hour course + final exam. Earn the badge for marketing materials.</td><td></td></tr><tr><td><h3><i class="fa-cubes" style="color:$primary;">:cubes:</i></h3></td><td><strong>Certified Evolve Developer</strong></td><td>Technical certification for solution and technology partners. 8-hour course + practical exam.</td><td></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>Certified Evolve Compliance</strong></td><td>Specialized track for partners working with regulated verticals. 6-hour course + case studies.</td><td></td></tr></tbody></table>
Certifications are valid for 12 months and renew with a 1-hour update course covering the year's changes.
<p><a href="https://gitbook.com" class="button primary">Open the learning portal</a></p>
## Self-paced courses
Free, available to all partner-portal users:
| Course | Length | For |
| --- | --- | --- |
| Evolve 101 | 1 hour | New partner team members |
| Selling Evolve | 2 hours | Sales reps and account executives |
| Connect for marketplaces | 3 hours | Solution partners building marketplaces |
| Identity for regulated verticals | 2 hours | Compliance officers and technical leads |
| Migrating from Stripe | 2 hours | Partners helping customers cut over |
## Live training and webinars
Monthly cadence:
* **First Tuesday** — Product update webinar (45 minutes). What shipped, what's coming.
* **Second Tuesday** — Office hours with the partner success team. Ask anything.
* **Third Tuesday** — Technical deep-dive (rotating topics). Engineering-focused.
* **Fourth Tuesday** — Sales enablement session. Pitches, objection handling, deal mechanics.
All sessions are recorded; recordings live in the recordings library below.
## Recordings
Past sessions:
* **Spring partner summit (March 2026)** — keynote, technical sessions, Q&A.
* **Connect for international platforms** (Feb 2026) — handling multi-country regulatory complexity.
* **Adverse media screening** (Feb 2026) — when to enable, how to tune, false-positive handling.
* **v3-beta walkthrough** (Jan 2026) — what's changing in the next major Payments API.
* **2025 retrospective** (Jan 2026) — what we shipped, what's next.
The full library has 200+ hours of recordings going back 3 years.
<p><a href="https://gitbook.com" class="button secondary">Browse the library</a></p>
## Sales enablement kit
For your sales team:
* Pitch deck templates (5 decks for different audiences).
* Objection-handling guides.
* Pricing calculators (rev share, customer-cost models).
* Comparison battle cards (Stripe, Adyen, in-house).
* Discovery question bank.
Distributed to certified partners; refresh quarterly. See the **Sales enablement** tab in the partner portal.
## Related
* [Marketing resources](marketing-resources.md) — brand kit and case studies.
* [Deal registration](deal-registration.md) — protect commission on deals you close.
* [Partner support](support.md) — your PSM contact and escalation paths.
{% endif %}
references/example-site/products/connect/.gitbook/includes/environments.md
---
title: Test and live environments
---
Evolve has two fully separate environments. They share no data — keys, customers, charges, and webhooks all exist independently in each.
| Environment | API base URL | Dashboard | Key prefix |
| --- | --- | --- | --- |
| Test | <code class="expression">space.vars.api_test</code> | <code class="expression">space.vars.dashboard_test</code> | `sk_test_` / `pk_test_` |
| Live | <code class="expression">space.vars.api_live</code> | <code class="expression">space.vars.dashboard_live</code> | `sk_live_` / `pk_live_` |
Test mode accepts only test fixture data — test cards, test bank accounts, test connected-account onboarding flows. Nothing leaves the test environment.
references/example-site/products/connect/.gitbook/vars.yaml
api_live: https://api.evolve.com
api_test: https://api.test.evolve.com
dashboard_live: https://dashboard.evolve.com
dashboard_test: https://dashboard.test.evolve.com
support_email: support@evolve.com
status_page: https://status.evolve.com
default_application_fee_pct: 2.0
references/example-site/products/connect/embedded-checkout/buyer-experience.md
---
icon: cart-shopping
description: What the buyer actually sees, from the storefront to the receipt.
---
# Buyer experience
The buyer doesn't know or care that your site is built on Connect — they're trying to pay the seller. Your job, as the platform, is to make sure that the parts of the experience the buyer notices feel like one coherent flow, even though there are three parties (buyer, seller, you) involved.
## The five moments
The buyer touches five things during a typical purchase. Each one is a place to get the platform vs seller framing right.
### <i class="fa-store" style="color:$primary;">:store:</i> 1. The storefront
Whatever page the buyer adds an item to a cart from. This is your platform's responsibility — a marketplace homepage, a SaaS app's billing page, a per-seller URL on your domain.
The storefront should make it clear who the seller is. The buyer should never reach checkout without knowing the brand they're paying.
### <i class="fa-credit-card" style="color:$primary;">:credit-card:</i> 2. The checkout
Where the card details get entered. Three integration shapes — see [Hosted vs embedded](hosted-vs-embedded.md). Whichever you pick, the checkout shows:
* The amount.
* The seller's name (and optionally logo).
* The supported payment methods.
* Whether the application fee is disclosed (often it isn't, but some marketplaces show it for transparency).
### <i class="fa-id-card" style="color:$primary;">:id-card:</i> 3. The card statement
The descriptor on the buyer's bank statement, weeks later. This is the most-disputed moment in any payments flow — buyers don't recognize the charge, call their bank, and a chargeback is born.
Most platforms use `PLATFORM*SELLER` — your platform name first (so the buyer knows which app to look in), then the seller's name (so they remember what they actually bought). Both must fit in the network's limit (Visa: 22 chars, Mastercard: 25 chars).
You can configure default behavior in **Settings → Connect → Statement descriptor** and override per-seller for sellers with strong brand recognition.
### <i class="fa-receipt" style="color:$primary;">:receipt:</i> 4. The receipt email
Sent from `receipts@evolve.com` (or your custom domain on Enterprise) immediately after payment. Contains:
* What was bought (description from the charge).
* Who they bought it from (seller's name).
* Who facilitated (your platform).
* A "Need help?" CTA pointing at the seller, the platform, or both — your call.
You can fully customize the template in **Settings → Connect → Email templates**, or replace it with your own outbound email and disable Evolve's default.
### <i class="fa-comments" style="color:$primary;">:comments:</i> 5. Support
When something goes wrong, the buyer reaches out. They might reach out to:
* The seller directly (their email or chat).
* Your platform's support.
* Their card-issuing bank (which means a dispute).
Make the first two easy and you'll get fewer of the third. Your platform's role is usually to **route** the support request to the right party — buyer questions about delivery go to the seller, questions about platform behavior or refunds initiated by the platform stay with you.
## Receipt customization
Three customization layers, each with a different set of decisions:
{% columns %}
{% column width="33%" %}
### <i class="fa-palette" style="color:$primary;">:palette:</i> Visual
Logo, brand colors, fonts. Can be platform-wide or per-seller. Most platforms use platform branding everywhere; some white-label per-seller.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-language" style="color:$primary;">:language:</i> Localization
Receipt language is set by the buyer's locale, with the seller's locale as a fallback. Evolve translates the structural text; the seller's product description stays in whatever language they wrote it.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-pen-to-square" style="color:$primary;">:pen-to-square:</i> Custom fields
Order numbers, fulfillment estimates, shipping addresses. Surface anything from your platform's data model on the receipt by configuring custom fields per charge.
{% endcolumn %}
{% endcolumns %}
## Avoiding common buyer-experience mistakes
<details>
<summary>Don't hide the seller's name on the storefront</summary>
Platforms that mask which seller the buyer is paying tend to see higher dispute rates — not because of fraud, but because the buyer doesn't recognize the charge later. Even if your platform is the dominant brand, show the seller's name somewhere on the page where the buyer adds to cart.
</details>
<details>
<summary>Don't surprise the buyer with a different brand at checkout</summary>
If the storefront is your platform's brand and the checkout suddenly says "Pay Acme Corp", buyers get confused and abandon. Either keep the platform brand visible at checkout, or make sure the seller is mentioned consistently from the storefront onward.
</details>
<details>
<summary>Don't bury the support contact</summary>
A buyer who can't find a way to ask for a refund will go to their bank instead. Surface a "Get help" link on the receipt and the storefront, ideally with the seller's contact and a "Or contact platform support" fallback.
</details>
## Related
* [Hosted vs embedded](hosted-vs-embedded.md) — choosing the integration shape.
* [Customization](customization.md) — themes, languages, and per-seller branding.
* [Refunds and disputes](../platform-setup/refunds-and-disputes.md) — what happens when the buyer asks for their money back.
references/example-site/products/connect/embedded-checkout/customization.md
---
icon: paint-roller
description: Brand the checkout — colors, fonts, logos, fields, and per-seller overrides.
---
# Customization
The default Connect checkout looks neutral and modern, with the seller's name and your platform's name in the right places. Most platforms customize at least the colors and logo to match their brand. Some go further — custom fonts, custom layouts, white-label per-seller branding.
This page covers what you can customize, where, and at which integration shape.
## Visual customization
What you control depends on the shape:
| | Hosted | Embedded | Direct API |
| --- | :---: | :---: | :---: |
| Logo | ✅ | ✅ | ✅ |
| Brand color (primary) | ✅ | ✅ | ✅ |
| Brand color (secondary) | ✅ | ✅ | ✅ |
| Custom font | — | ✅ | ✅ |
| Field layout | — | ✅ | ✅ |
| Custom CSS / classes | — | Limited | ✅ |
| Background image | ✅ | ✅ | ✅ |
Settings live in **Connect → Branding** in the dashboard. Changes apply immediately on hosted and embedded; for direct API integrations, they're hints — your code does the actual rendering.
## Per-seller branding
By default, all sellers on your platform use the platform's branding. For sellers with strong brands of their own — established stores on a marketplace, B2B sellers under enterprise contracts — you can let them override:
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="layer-group" %}
**You're on Enterprise** — full per-seller branding (including custom domains and per-seller CSS) is available. Configure in **Connect → Branding → Per-seller overrides**.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="layer-group" %}
**You're on Growth** — per-seller logo and brand color overrides are available. Custom fonts and CSS require Enterprise.
{% endhint %}
{% endif %}
| Override | Growth | Enterprise |
| --- | :---: | :---: |
| Per-seller logo | ✅ | ✅ |
| Per-seller brand colors | ✅ | ✅ |
| Per-seller font | — | ✅ |
| Per-seller CSS | — | ✅ |
| Per-seller subdomain (`acme.checkout.evolve.com`) | — | ✅ |
| Custom domain (`pay.acme.com`) | — | ✅ |
## Custom fields
The default checkout collects:
* Card details (number, expiry, CVC).
* Billing name and ZIP.
* Email (for the receipt).
You can add custom fields per session — order numbers, fulfillment dates, dietary preferences, anything your platform's data model needs:
{% columns %}
{% column width="50%" %}
### <i class="fa-text-width" style="color:$primary;">:text-width:</i> Text fields
Free-text input. Validation rules: required, min/max length, regex.
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-list-check" style="color:$primary;">:list-check:</i> Dropdowns
Predefined options. Useful for shipping methods, gift wrap, etc.
{% endcolumn %}
{% endcolumns %}
{% columns %}
{% column width="50%" %}
### <i class="fa-square-check" style="color:$primary;">:square-check:</i> Checkboxes
Single boolean. Common use: opt-in to marketing or terms acceptance.
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-calendar" style="color:$primary;">:calendar:</i> Date pickers
For delivery dates, appointments, gift-card send dates.
{% endcolumn %}
{% endcolumns %}
Custom field values land on the charge as metadata, visible in the dashboard, on receipts, in webhook payloads, and on the [settlement file](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/settlement-files).
## Localization
The hosted and embedded checkouts auto-detect the buyer's locale from their browser and translate the structural text — labels, error messages, button copy — into 30+ languages. The seller's product description and the platform's branded text stay in whatever language you provide.
Override the auto-detection by setting `locale` on the checkout session:
```http
POST /v1/checkout_sessions
{
"amount": 4200,
"currency": "usd",
"locale": "fr"
}
```
For languages not in the default 30, Enterprise customers can supply a translation file and Evolve will use it.
## Receipts
The receipt email template is customizable separately from the checkout itself. Three layers:
1. **Template content** — what shows up in the email body.
2. **From address** — `receipts@evolve.com` by default; can be your own domain.
3. **Reply-to** — defaults to your platform's support; can be set per-seller.
Configure under **Connect → Email templates**.
## What you can't customize
A few things are locked, for compliance and trust reasons:
* The list of payment methods Evolve supports — sellers can't add a network we don't process.
* The card-collection iframe in embedded — the actual `<input>`s are Evolve's, for PCI scope reasons.
* The "Powered by Evolve" footer on hosted checkouts (Growth plan); removable on Enterprise.
* The receipt's required legal disclosures (varies by region).
## Related
* [Hosted vs embedded](hosted-vs-embedded.md) — what's available at each shape.
* [Buyer experience](buyer-experience.md) — what the customizations look like to the buyer.
references/example-site/products/connect/embedded-checkout/hosted-vs-embedded.md
---
icon: route
description: Three integration shapes for the Connect checkout — and how to pick.
---
# Hosted vs embedded
There are three ways to put a Connect checkout in front of buyers. They share the same backend (same fees, same payouts, same dispute flow) — the differences are in how much engineering you take on and how much control you keep over the buyer's experience.
## The three shapes
{% columns %}
{% column width="33%" %}
### <i class="fa-link" style="color:$primary;">:link:</i> Hosted
A URL you redirect the buyer to. Evolve hosts the page; you don't render anything.
* **Setup time:** under an hour.
* **PCI scope:** zero.
* **Customization:** logo, colors, language.
* **URL:** `checkout.evolve.com/c/...`
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-window-maximize" style="color:$primary;">:window-maximize:</i> Embedded
Evolve's checkout rendered inside a div on your site, with your URL.
* **Setup time:** a day.
* **PCI scope:** SAQ A.
* **Customization:** layout, fields, full theme control.
* **URL:** stays yours.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-code" style="color:$primary;">:code:</i> Direct API
Build the checkout from scratch. You handle the card collection.
* **Setup time:** weeks.
* **PCI scope:** SAQ D (highest).
* **Customization:** total.
* **URL:** stays yours.
{% endcolumn %}
{% endcolumns %}
## Which to pick
A short decision tree:
```mermaid
flowchart LR
Q1{Need URL<br>to stay yours?} -->|No| Hosted
Q1 -->|Yes| Q2{Need full UI<br>control?}
Q2 -->|No| Embedded
Q2 -->|Yes| Q3{Have PCI<br>compliance team?}
Q3 -->|No| Embedded
Q3 -->|Yes| Direct[Direct API]
```
In our experience, the right starting choice for most platforms:
| You are... | Start with |
| --- | --- |
| A new platform under 6 months old | Hosted |
| An established platform under $10M GMV | Hosted or embedded |
| An established platform $10M+ GMV with brand standards | Embedded |
| A regulated platform that needs total control | Direct API |
You can migrate from hosted to embedded later without changing your seller onboarding or payout setup — they're independent layers.
## Hosted in detail
Hosted is what you get on the [Connect Quickstart](../quickstart/onboard-your-first-seller.md) by default. The flow:
{% stepper %}
{% step %}
### Your server creates a checkout session
You call `POST /v1/checkout_sessions` with the seller's connected account, the amount, the application fee, and a `success_url` and `cancel_url` to redirect to.
{% endstep %}
{% step %}
### You redirect the buyer
The response gives you a URL like `https://checkout.evolve.com/c/cs_3KsM12pL9q`. Redirect the buyer there.
{% endstep %}
{% step %}
### Evolve hosts the rest
The buyer sees the checkout, pays, and is redirected to your `success_url` (or `cancel_url`). The session ID is appended so your server can confirm what happened.
{% endstep %}
{% endstepper %}
This is the lowest-PCI-scope, lowest-engineering option. It's also the option most platforms launch with and stay on.
## Embedded in detail
Embedded gives you the same backend but renders the checkout inside your own page, using a JavaScript SDK and an iframe. The card collection still happens in an Evolve-hosted iframe (so your PCI scope stays SAQ A), but the surrounding chrome is yours.
```html
<div id="evolve-checkout"></div>
<script src="https://js.evolve.com/v1/connect.js"></script>
<script>
const evolve = Evolve('pk_test_...');
evolve.mountCheckout('#evolve-checkout', {
sessionId: 'cs_3KsM12pL9q',
});
</script>
```
The session is created server-side the same way as for hosted, but instead of redirecting, you mount it on your page.
## Direct API in detail
Direct API integrations build the entire checkout from scratch — your form, your field validation, your payment-method selection. The card details still pass through a JavaScript Element so they don't touch your servers, but everything around them is yours.
This is the right choice for:
* Mobile apps (where you'd render checkout inside the app).
* Highly customized B2B onboarding flows.
* Platforms with PCI compliance teams who want every pixel under their control.
Most platforms don't need this level of control and pay for it in engineering time. See [Developers / Connect API](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/connect-api) for the API reference.
## Per-seller variation
Different sellers may want different checkout shapes. A high-volume marketplace seller might want their own subdomain and embedded flow; a long-tail seller might be fine with hosted. Connect supports both — you can configure the default at the platform level and override per-seller in the connected account record.
The override is just a setting; the buyer doesn't know which shape any given seller is using.
## Related
* [Buyer experience](buyer-experience.md) — what the buyer sees regardless of shape.
* [Customization](customization.md) — themes, languages, fields.
* [Splitting payments](../platform-setup/splitting-payments.md) — application fees configured at session creation.
references/example-site/products/connect/embedded-checkout/README.md
---
icon: window-maximize
description: The buyer-facing flow on a Connect platform — hosted, embedded, or fully custom.
---
# Embedded checkout
The checkout is where the buyer pays the seller. On a Connect platform, the checkout looks slightly different than on a single-merchant Payments site — the receipt mentions both the platform and the seller, the application fee may be disclosed, and refunds flow back through the platform.
You have three integration shapes to choose from. Most platforms start with **hosted** for speed and migrate to **embedded** once they want the URL to stay branded.
## Three shapes
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-link" style="color:$primary;">:link:</i></h3></td><td><strong>Hosted</strong></td><td>Redirect to an Evolve-hosted page. Fastest setup, fewest knobs.</td><td><a href="hosted-vs-embedded.md">hosted-vs-embedded.md</a></td></tr><tr><td><h3><i class="fa-window-maximize" style="color:$primary;">:window-maximize:</i></h3></td><td><strong>Embedded</strong></td><td>Drop-in checkout inside your own site. URL stays yours.</td><td><a href="hosted-vs-embedded.md">hosted-vs-embedded.md</a></td></tr><tr><td><h3><i class="fa-paint-roller" style="color:$primary;">:paint-roller:</i></h3></td><td><strong>Customization</strong></td><td>Theme, fields, language, and the seller branding shown.</td><td><a href="customization.md">customization.md</a></td></tr></tbody></table>
## What the buyer sees
```mermaid
flowchart LR
A[Browse seller's<br>storefront] --> B[Click 'Buy']
B --> C[Checkout opens]
C --> D[Enter payment<br>details]
D --> E[Confirm]
E --> F[Receipt + thank-you]
```
A typical buyer experience takes 30–60 seconds. The moments worth thinking about as a platform:
{% columns %}
{% column width="50%" %}
### <i class="fa-id-card" style="color:$primary;">:id-card:</i> Whose name is on the statement?
The descriptor on the buyer's card statement can be either the platform's, the seller's, or a combination. Most platforms use `PLATFORM*SELLER` — your platform name first, then the seller's name. Configurable per-seller.
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-receipt" style="color:$primary;">:receipt:</i> Whose receipt do they get?
Receipts come from `receipts@evolve.com` on the platform's behalf, with the seller's name and your platform name as co-senders. Customers reply-to defaults to the seller's support address (configurable).
{% endcolumn %}
{% endcolumns %}
{% columns %}
{% column width="50%" %}
### <i class="fa-rotate-left" style="color:$primary;">:rotate-left:</i> Where do refund requests go?
Most platforms surface a "Refund" button on the seller's storefront, not on the receipt. The customer asks the seller for a refund; the seller issues it from your platform's UI. See [Refunds and disputes](../platform-setup/refunds-and-disputes.md).
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i> Who handles disputes?
Disputes are filed against the platform's merchant ID (since you're the merchant of record), but the platform usually delegates them to the seller — with a deadline and a default outcome if the seller doesn't respond. Configurable in **Settings → Connect → Disputes**.
{% endcolumn %}
{% endcolumns %}
## What the seller sees
In their portal (whether you build it or use Evolve's hosted seller dashboard), each seller sees:
* The transactions they were the seller on.
* The application fees taken out.
* Their balance and upcoming payouts.
* Any open disputes assigned to them.
They do **not** see other sellers' transactions or platform-level reporting.
## Where to start
* **If you've never integrated payments** — start with [Buyer experience](buyer-experience.md) to get the high-level mental model.
* **If you're choosing an integration** — read [Hosted vs embedded](hosted-vs-embedded.md).
* **If you've decided on embedded** — head straight to [Customization](customization.md).
## Related
* [Onboarding sellers](../platform-setup/onboarding-sellers.md) — what happens before a seller can take payments.
* [Splitting payments](../platform-setup/splitting-payments.md) — how application fees and routing work.
* [Refunds and disputes](../platform-setup/refunds-and-disputes.md) — what happens after a payment.
references/example-site/products/connect/platform-setup/onboarding-sellers.md
---
icon: user-plus
description: How a seller goes from "signed up to your platform" to "ready to take payments."
---
# Onboarding sellers
Before a seller can take payments, Evolve has to know who they are — legal name, banking details, identity proof. This is the seller-onboarding flow, and it's the first place most platforms spend engineering effort on Connect.
The flow uses [Identity verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows) under the hood — Connect adds the connected-account specifics on top. You don't build the verification logic; you decide what data to collect, who collects it (the seller in a hosted flow, your platform in a programmatic flow), and what happens when something fails.
## What gets collected
Every connected account requires:
{% columns %}
{% column width="50%" %}
### <i class="fa-id-card" style="color:$primary;">:id-card:</i> Legal identity
* Legal name (individual or business).
* Date of birth (individuals) or formation date (businesses).
* Government ID number (SSN, EIN, or country equivalent).
* Address.
For businesses, plus [beneficial ownership](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/business/beneficial-ownership) for owners ≥25%.
{% endcolumn %}
{% column width="50%" %}
### <i class="fa-building-columns" style="color:$primary;">:building-columns:</i> Banking
* Bank account for payouts.
* Account holder name (must match the legal entity).
* Verification — Plaid instant or micro-deposits ([details](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/bank-account)).
For non-US banks, plus tax forms (W-9 for US, W-8 for non-US).
{% endcolumn %}
{% endcolumns %}
For some seller types and regions, additional fields are required — you'll see these prompted as part of the hosted onboarding flow when relevant.
## Three onboarding shapes
Pick one based on how much control you want:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-link" style="color:$primary;">:link:</i></h3></td><td><strong>Hosted</strong></td><td>Seller fills in everything on an Evolve-hosted page. Fastest to launch.</td><td></td></tr><tr><td><h3><i class="fa-window-maximize" style="color:$primary;">:window-maximize:</i></h3></td><td><strong>Embedded</strong></td><td>Same fields, rendered inside your site. Your URL.</td><td></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>Programmatic</strong></td><td>Your code submits the data — for sellers who've already given it to you elsewhere.</td><td></td></tr></tbody></table>
Most platforms launch with hosted, then move to embedded once they're confident in their seller-onboarding UX.
## The seller's experience (hosted)
```mermaid
flowchart LR
Invite[Invite email] --> Form[Hosted form]
Form --> ID[Identity verification]
ID --> Bank[Bank verification]
Bank --> Done[Ready to take payments]
```
Typical timing:
| Step | How long |
| --- | --- |
| Form (legal info, address) | 3–5 min |
| Identity verification (document + selfie) | 1–2 min |
| Bank verification (Plaid) | 1 min |
| Bank verification (micro-deposits) | 1–2 days |
| Manual review (if triggered) | 1 hour to 1 business day |
Most US individuals complete in under 10 minutes end to end if they pass everything on the first try.
## Pre-fill from your platform
Most platforms already have some of the data Evolve needs — the seller's name, email, and address from your sign-up flow. Pre-fill those into the hosted form so the seller doesn't re-enter them.
You pre-fill at session creation time. The seller can still edit the values; pre-filling just saves them typing.
## What happens when something fails
A failed onboarding doesn't mean a permanently rejected seller — most failures are recoverable.
<details>
<summary>Identity verification fails</summary>
Reasons: expired document, document tampering signal, selfie mismatch. The seller sees a clear error and can retry up to 3 times in 24 hours. If they're still stuck, your team can manually review from the dashboard.
</details>
<details>
<summary>Bank verification fails</summary>
Plaid couldn't reach the bank, or the customer entered wrong micro-deposit amounts. The flow falls back to the alternate method ([Plaid → micro-deposits](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/bank-account/micro-deposits), or vice versa) automatically.
</details>
<details>
<summary>Sanctions match</summary>
Rare but serious. The seller can't be onboarded. The dashboard shows the match details to your compliance team; depending on the match confidence, either you reject the seller or escalate to manual review.
</details>
<details>
<summary>Tax form mismatch</summary>
The name on the tax form doesn't match the business legal name. Usually a typo on either side. The seller can re-upload.
</details>
## Re-onboarding when info changes
Sellers' info isn't static. They move, change banks, restructure their business. When this happens:
* **Bank account changes** — the seller updates from their portal; verification re-runs automatically.
* **Address changes** — accepted up to a small threshold of the original; large changes trigger re-verification.
* **Beneficial owner changes** — re-verification required for the new owners (KYB only).
The dashboard surfaces a "Profile changed" badge on the seller until the re-verification completes, and you can hold their payouts during the change if your risk policy requires it.
## Bulk onboarding
For platforms migrating from another payment provider, Evolve supports bulk onboarding — you upload a CSV of seller data, Evolve generates onboarding URLs, and you email them out from your side. The flow is the same hosted experience for the seller; the difference is just how the URLs get generated.
Bulk onboarding is usually a one-shot during platform launch or migration. Day-to-day, you'll create connected accounts as sellers sign up.
## Related
* [Connect quickstart](../quickstart/onboard-your-first-seller.md) — walkthrough with a test seller.
* [Identity verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/identity-verification) — the verification engine behind onboarding.
* [Bank account verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/bank-account) — the bank-side details.
* [Beneficial ownership](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/business/beneficial-ownership) — for business sellers.
references/example-site/products/connect/platform-setup/payouts.md
---
icon: money-bill-transfer
description: How sellers' balances become payouts to their bank accounts.
---
# Payouts to sellers
Each seller has their own balance on Evolve, fed by transfers from charges they've completed (minus your application fees and any refunds). That balance pays out to their verified bank account on a schedule — daily, weekly, monthly, or on demand.
The platform doesn't touch this money. Once it's transferred to the seller's connected-account balance, it's their funds; they get paid out automatically and you can't pull it back unless you reverse a specific transfer.
## Default schedules
Three schedule shapes:
| Schedule | When sellers get paid | Best for |
| --- | --- | --- |
| **Daily** | T+1 (next business day after capture) | Marketplaces, food delivery, anything where seller cash flow matters |
| **Weekly** | Every Monday for the prior week | Subscription platforms, B2B SaaS |
| **Monthly** | 1st of the month for the prior month | Long-tail marketplaces, royalty payouts |
| **Manual** | When you trigger it | Platforms with custom scheduling |
You set the platform-wide default in **Connect → Settings → Default payout schedule**. Sellers can override their own schedule from their portal (within the bounds you allow).
## What flows through
The seller's balance equals everything in their favor minus everything against:
```
Charge transfers
- Refund deductions
- Dispute deductions (if assigned to seller)
- Direct charges from platform (if any)
+ Direct credits from platform
= Seller balance
```
When the schedule runs, the seller's full positive balance pays out — minus any reserves you've configured.
## Reserves
Some platforms hold a percentage of the seller's balance for a rolling window — common in marketplaces with high dispute exposure or platforms launching with new sellers.
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="info" %}
**Enterprise customers** can configure per-seller reserves with custom rules — e.g. "5% rolling reserve for new sellers, dropping to 0% after 90 days." Set in **Connect → Settings → Reserves**.
{% endhint %}
{% endif %}
A typical reserve config:
| Setting | Common value |
| --- | --- |
| **Reserve percentage** | 5–10% |
| **Reserve window** | 90 days rolling |
| **Applies to** | All new sellers in their first 90 days |
The reserve sits on the seller's balance but isn't paid out. Each day, the oldest 1/90th rolls off (90-day window) and becomes payable. Disputes and refunds during the window come out of the reserve before they hit the seller's payout.
The seller can see their reserved balance separately from their payable balance in their portal.
## On-demand payouts
For platforms where seller cash flow really matters — gig-economy apps, instant-pay marketplaces — you can offer **on-demand payouts**: a button the seller taps when they want their money now, before the next scheduled payout.
On-demand payouts cost an extra 1% of the payout amount, charged to the seller (or absorbed by the platform — your call). They land in the seller's bank within minutes if the bank supports RTP/FedNow, otherwise within hours.
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="bolt" %}
**Enterprise customers** can configure on-demand payouts and decide whether the fee is paid by the seller, the platform, or split. Configure in **Connect → Settings → Instant payouts**.
{% endhint %}
{% endif %}
## Holds
Three reasons a seller's payout might be held:
<details>
<summary>Risk hold</summary>
Evolve's risk system flagged something — unusually high transaction volume, dispute pattern, or specific signals from the network. The seller's payouts pause for up to 14 days while the case is reviewed. The seller and platform both get notified.
</details>
<details>
<summary>Platform hold</summary>
Your platform has held the seller manually — usually because of a complaint, a TOS violation, or a pending investigation. You set the duration; the dashboard shows your team is responsible for releasing it.
</details>
<details>
<summary>Compliance hold</summary>
A re-verification is required (e.g. document expired, address change pending). Payouts pause until verification completes. Usually resolved within 1–2 days.
</details>
In all three cases, charges keep running normally — the seller can still take payments. Only the payout to their bank is paused.
## Failed payouts
A scheduled payout can fail if the seller's bank account becomes invalid (closed, frozen, or wrong details). When this happens:
1. The payout is marked **Failed** with the bank's reason code.
2. The seller and platform get an email.
3. The amount returns to the seller's balance.
4. The seller updates their bank account in their portal; the next scheduled payout includes the failed amount.
Repeated failures (three in a row) auto-pause the seller's payouts and require platform intervention.
## What you see as the platform
The platform dashboard shows aggregate payout activity:
* **Today's payouts** — total amount across all sellers, by currency.
* **Pending payouts** — what's queued for the next schedule run.
* **Held payouts** — broken down by hold type, with the action item to clear each.
* **Failed payouts** — recent failures, with one-click resend.
For the per-seller view, click into the seller and see their payout history alongside their charges and balance.
## Reporting
Two payout reports under **Reports** when Connect is enabled:
* **Platform payout summary** — daily/weekly/monthly aggregates of total payouts.
* **Per-seller payout history** — drill-down per seller for support purposes.
Both export to CSV and can be [scheduled](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reporting/sharing-exports).
## Related
* [Splitting payments](splitting-payments.md) — how each transfer's amount is determined.
* [Refunds and disputes](refunds-and-disputes.md) — how reversals affect payouts.
* [Money movement (Payments)](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/money-movement) — the underlying settlement engine.
references/example-site/products/connect/platform-setup/README.md
---
icon: sliders
description: The platform-side configuration — onboarding sellers, splitting payments, paying them out, and handling disputes.
---
# Platform setup
This section covers everything that happens on the platform side of Connect — separate from the buyer-facing [embedded checkout](../embedded-checkout/README.md). These are the configurations and workflows that run *behind* the buyer experience: how sellers join your platform, how each payment is split, how sellers get paid out, and how you handle the messy moments (refunds, disputes, terminations).
Most of this is configured once at platform launch and tweaked occasionally as your business scales. The exception is the daily operational work — issuing refunds, responding to disputes, onboarding new sellers — which lives in the same dashboards your team uses every day.
## The four jobs
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-user-plus" style="color:$primary;">:user-plus:</i></h3></td><td><strong>Onboarding sellers</strong></td><td>Get a seller from "signed up" to "ready to take payments."</td><td><a href="onboarding-sellers.md">onboarding-sellers.md</a></td></tr><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Splitting payments</strong></td><td>Application fees, flat fees, conditional rules.</td><td><a href="splitting-payments.md">splitting-payments.md</a></td></tr><tr><td><h3><i class="fa-money-bill-transfer" style="color:$primary;">:money-bill-transfer:</i></h3></td><td><strong>Payouts to sellers</strong></td><td>Schedules, bank routing, holds.</td><td><a href="payouts.md">payouts.md</a></td></tr><tr><td><h3><i class="fa-rotate-left" style="color:$primary;">:rotate-left:</i></h3></td><td><strong>Refunds and disputes</strong></td><td>Who issues, who pays, who responds.</td><td><a href="refunds-and-disputes.md">refunds-and-disputes.md</a></td></tr></tbody></table>
## How the money flows
```mermaid
flowchart LR
Buyer[Buyer] -->|pays| Charge[Charge<br>on platform]
Charge --> Split{Split}
Split -->|application fee| Platform[Platform<br>balance]
Split -->|net| SellerBal[Seller<br>balance]
SellerBal -->|payout| SellerBank[Seller bank<br>account]
Platform -->|payout| PlatformBank[Platform bank<br>account]
```
Each charge produces three financial movements on Evolve's side:
1. **Authorization and capture** of the buyer's card — handled by [Payments](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/payment-lifecycle).
2. **Application fee** to the platform's Evolve balance.
3. **Transfer** of the net amount to the seller's connected-account balance.
Both balances pay out independently on their own schedules. The platform's balance settles like any single-merchant Payments balance; each seller's balance settles to their own bank account on the schedule you set for them.
## Roles on your team
Connect introduces a few platform-specific roles you may want to set up:
| Role | What they do |
| --- | --- |
| **Seller success** | Helps new sellers complete onboarding, troubleshoots verification failures. |
| **Operations** | Issues refunds the seller can't or won't, escalates disputes, manages risk holds. |
| **Compliance** | Reviews flagged sellers, manages seller terminations, runs periodic re-KYC. |
| **Finance** | Reconciles the platform's settlement, handles platform-level reporting. |
You can configure granular dashboard permissions per role in **Settings → Team → Roles**. Most platforms give Seller Success access to seller details and onboarding flow, but not to platform-level financial data.
## Platform reporting
Two new report types appear in **Reports** when Connect is enabled:
* **Per-seller report** — every charge, refund, fee, and payout for a specific seller. Useful for support and per-seller close-out.
* **Application fees report** — total fees earned across all sellers, by day, by seller cohort, by transaction shape. This is your platform's revenue line.
Both reports are exportable via [scheduled exports](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reporting/sharing-exports), the same way Payments reports are.
## Plan considerations
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="layer-group" %}
**You're on Growth** — up to 100 connected accounts. The full Connect feature set is available, with the volume cap as the only differentiator from Enterprise.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="layer-group" %}
**You're on Enterprise** — unlimited accounts, plus white-label embedded checkout, custom onboarding workflows, and consolidated KYC across all sellers. Talk to your account team for the platform-launch playbook.
{% endhint %}
{% endif %}
## Related
* [Connect overview](../README.md) — high-level on what Connect is.
* [Payments](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/) — Connect inherits everything from Payments.
* [Identity verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows) — used for seller onboarding.
references/example-site/products/connect/platform-setup/refunds-and-disputes.md
---
icon: rotate-left
description: Who issues refunds, who responds to disputes, and how the money moves when a payment reverses.
---
# Refunds and disputes
When a payment reverses — the buyer asks for their money back, or the cardholder disputes the charge — the question on a Connect platform is: **who pays?** The buyer always gets their money back; the question is whether the seller's balance covers it, the platform's balance does, or some combination.
Most platforms have a default policy and override it case-by-case. This page covers what's possible and how to configure it.
## Refunds
Refunds on Connect work like [Payments refunds](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/refunds), with one extra decision: how the refund is split between the platform and the seller.
### The default split
By default, a refund mirrors the original payment's split — proportionally:
```
Original payment: $100, with a $5 application fee.
Full refund: -$100, with -$5 from the platform balance and -$95 from the seller balance.
```
For partial refunds, the platform fee is reduced proportionally:
```
Original payment: $100, with a $5 application fee.
$25 refund: -$25, with -$1.25 from the platform and -$23.75 from the seller.
```
This is the "fair" default — both parties give back proportional amounts.
### Override: keep the application fee
Sometimes the platform shouldn't bear refund cost — for example, if the seller is responsible for the issue (defective product, late shipment) and the seller-side terms say so. Set `refund_application_fee: false`:
```
Original payment: $100, with a $5 application fee.
Full refund: -$100, all from the seller balance ($95) plus the seller absorbs the $5 fee.
```
The seller's balance goes down by the full $100. The platform's application fee stays.
### Override: platform covers everything
The reverse — platform absorbs the full refund cost. Useful for goodwill refunds the platform issues at its own discretion. The platform's balance goes down by $100 and the seller is not affected.
This is technically two separate operations: a refund (platform-funded) and no transfer reversal. The platform pays out $100 from its balance directly to the buyer's card.
### Who can issue refunds
By default, both the platform and the seller can issue refunds against any payment. You can restrict this:
* **Platform-only refunds** — sellers can't issue refunds; they must request the platform to do it. Common for marketplaces where the platform handles all customer service.
* **Seller-only refunds** — sellers handle their own refunds; platform stays out of the loop. Common for B2B platforms where the seller has a direct relationship with the buyer.
* **Either** (default) — both can issue. The first one to act takes the action.
Configure in **Connect → Settings → Refund permissions**.
## Disputes
Disputes are more complicated than refunds because the timeline is longer (20+ days for evidence submission, 30–75 days for the decision) and the cost includes both the disputed amount and a $15 dispute fee.
### Who's the merchant of record
On Connect, the **platform is the merchant of record** for card-network purposes. This means:
* Disputes are filed against the platform's merchant ID.
* The platform's [dispute rate](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/disputes) is what the network monitors.
* Platform-level dispute thresholds (1.0% under most network rules) apply across all sellers' charges combined.
A bad seller can drag down the platform's overall dispute rate, which is why most platforms care about dispute handling and seller risk management.
### Who pays
Three policies for who absorbs the disputed amount and fee:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-store" style="color:$primary;">:store:</i></h3></td><td><strong>Pass to seller</strong></td><td>Seller's balance covers the disputed amount + fee. Most common.</td><td></td></tr><tr><td><h3><i class="fa-circles-overlap" style="color:$primary;">:circles-overlap:</i></h3></td><td><strong>Platform absorbs</strong></td><td>Platform balance covers everything. Used for premium-tier sellers.</td><td></td></tr><tr><td><h3><i class="fa-scale-balanced" style="color:$primary;">:scale-balanced:</i></h3></td><td><strong>Split</strong></td><td>Platform takes the fee, seller takes the disputed amount. The "fair" default.</td><td></td></tr></tbody></table>
You can set the policy at the platform default and override per-seller.
### Who responds
The platform always *can* respond to a dispute (since the dispute is technically against the platform). The question is whether you want to.
Two patterns:
**Platform handles all disputes.** The platform's ops team owns the dispute queue. They gather evidence (often by asking the seller for it), submit, and accept the outcome. Common for platforms that already handle customer support centrally.
**Delegate to seller.** The platform forwards the dispute to the seller via email and the seller's portal. The seller has X days (you choose; we recommend 7) to submit evidence; if they don't respond, the platform either accepts the dispute or submits empty evidence. Common for marketplaces where the seller has the actual product and shipping records.
### Dispute timeline
```mermaid
flowchart LR
A[Cardholder disputes] --> B[Platform notified]
B --> C[Funds withdrawn<br>from balance]
C --> D{Who responds?}
D -->|Platform| E[Submit evidence]
D -->|Seller| F[Forward to seller]
F --> E
E --> G{Network reviews}
G -->|Won| H[Funds returned]
G -->|Lost| I[Funds final]
```
The total clock — dispute opens to outcome — is typically 30–75 days. During that time, the disputed funds are withheld from the responsible balance (per the policy above).
## Best practices
Three things that consistently reduce dispute costs on Connect platforms:
<details>
<summary>Make refunds easy for buyers</summary>
A buyer who can't get a refund through your platform will go to their bank instead. A bank chargeback costs $15 in fees and damages your dispute rate; a platform refund costs $0 in fees and doesn't.
Surface a "Get a refund" link on the receipt and on the seller's storefront. Make the seller's policy clear at checkout.
</details>
<details>
<summary>Keep an eye on per-seller dispute rates</summary>
A small number of bad-actor sellers can drag down the whole platform's dispute rate. The per-seller dispute report shows you who's costing you. Set a threshold — typically 1% — above which sellers are paused for review.
</details>
<details>
<summary>Set evidence-submission expectations with sellers at onboarding</summary>
Sellers who haven't been told they're on the hook for disputes don't gather evidence well. The onboarding emails include a brief "what happens if a buyer disputes" doc; surface it again in your seller-facing portal so it's findable when they need it.
</details>
## Related
* [Splitting payments](splitting-payments.md) — how the original split affects the refund split.
* [Payouts to sellers](payouts.md) — how disputes and refunds delay payouts.
* [Disputes (Payments)](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/disputes) — the underlying dispute mechanics.
references/example-site/products/connect/platform-setup/splitting-payments.md
---
icon: percent
description: Application fees, flat fees, and conditional rules — how every payment gets split between platform and seller.
---
# Splitting payments
Every Connect payment gets split between you and the seller. The default split is a percentage of the gross amount as your **application fee**, with the remainder transferred to the seller's connected-account balance. Most platforms start with a flat percentage — <code class="expression">space.vars.default_application_fee_pct</code>% is the default — and add complexity over time.
This page covers what you can configure and how to think about it.
## Three components of a split
A single payment can have up to three platform-side line items:
{% columns %}
{% column width="33%" %}
### <i class="fa-percent" style="color:$primary;">:percent:</i> Application fee
A percentage of the gross amount. The most common revenue model for platforms.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-dollar-sign" style="color:$primary;">:dollar-sign:</i> Flat fee
A fixed amount per transaction. Useful for covering processing costs.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-coins" style="color:$primary;">:coins:</i> Pass-through fee
Where the seller pays the processing fee. Less common but supported.
{% endcolumn %}
{% endcolumns %}
The combination is fully flexible — you can have all three on a single payment, or just one, or none (rare but possible for promotional payments).
## A simple example
A buyer pays $100 on a marketplace where the platform charges a 5% application fee plus a $0.50 flat fee:
| Line item | Amount |
| --- | --- |
| Buyer's card charged | $100.00 |
| Card processing fee (Evolve) | -$2.90 |
| Application fee (platform) | -$5.00 |
| Flat fee (platform) | -$0.50 |
| Net to seller | $91.60 |
The platform's balance goes up by $5.50 ($5.00 + $0.50). The seller's balance goes up by $91.60. The processing fee is netted from the platform's side by default — but you can configure it to net from the seller's side instead (see [Pass-through fees](#pass-through-fees) below).
## Where you configure the split
Two places, depending on whether the split is per-payment or platform-wide.
### Platform default
In **Connect → Settings → Default split**, set the platform-wide default:
* Application fee percentage — applies to all charges unless overridden per-session.
* Flat fee amount — applies on top of the percentage.
* Currency-specific overrides — different rates per currency.
This default is what gets used on every payment unless you specify otherwise at session creation.
### Per-payment override
When you create a [checkout session](../embedded-checkout/hosted-vs-embedded.md), you can override the split:
```http
POST /v1/checkout_sessions
{
"amount": 10000,
"currency": "usd",
"connected_account": "acct_3KsM12pL9q",
"application_fee_amount": 700,
"application_fee_currency": "usd"
}
```
This is the right place to put rules that depend on the specific transaction — different rates per product category, promotional discounts, deal-specific contracts.
## Conditional rules
Most platforms outgrow flat percentages. Common rules platforms add:
| Rule | Example |
| --- | --- |
| **By seller tier** | Top sellers pay 3%, standard sellers pay 5%, new sellers pay 7% during their first 90 days. |
| **By product category** | Digital goods 8%, physical goods 5%, services 12%. |
| **By transaction size** | 5% under $1,000; 3% from $1,000 to $10,000; 1.5% above $10,000. |
| **By currency** | 4% USD, 4.5% EUR, 5% other (covering FX margin). |
| **By time** | Black Friday promo — 0% application fee on a single weekend. |
You build these rules in your own code at session creation — Evolve doesn't have a built-in rules engine, since the right rules tend to be specific to the platform's business model. The rules are simple enough that platforms typically encode them as a function in their checkout-session-creation path.
## Pass-through fees
By default, the **card processing fee** (the 2.9% + $0.30 from the [Payments fee structure](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/fees-and-pricing)) comes off the platform's application fee. That means if you charge a 5% application fee on a $100 payment:
* Buyer pays $100.
* Seller gets $95.
* Platform gets $5 - $2.90 = $2.10. *(Processing comes off your side.)*
You can flip this so the seller absorbs the processing fee instead — `application_fee_includes_processing: false` on the session:
* Buyer pays $100.
* Seller gets $95 - $2.90 = $92.10. *(Processing comes off the seller's side.)*
* Platform gets $5.00.
Marketplaces that compete on take-rate often pass processing through to sellers; platforms that compete on seller experience usually absorb it.
## Refunds and the split
When a payment is refunded, the application fee is refunded **proportionally** by default. A full refund takes back the platform's full application fee; a partial refund takes back a proportional amount.
You can override this — for example, keep the application fee on a refund (the seller is bearing the full refund cost) — by specifying `refund_application_fee: false` on the refund. See [Refunds and disputes](refunds-and-disputes.md).
## What appears in reporting
Application fees show up in three places:
* **Per-payment**, on the charge timeline.
* **Per-seller**, on the seller's revenue summary.
* **Platform-wide**, in the application-fees report (your platform's revenue line).
The application-fees report is the most-watched dashboard for most Connect platforms — it's the daily revenue chart your CFO checks.
## Related
* [Onboarding sellers](onboarding-sellers.md) — set per-seller defaults during onboarding.
* [Payouts to sellers](payouts.md) — how the seller's balance becomes their payout.
* [Refunds and disputes](refunds-and-disputes.md) — what happens to the split when a payment reverses.
* [Fees and pricing (Payments)](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/fees-and-pricing) — the underlying processing fees.
references/example-site/products/connect/quickstart/onboard-your-first-seller.md
---
icon: rocket
description: Onboard a test seller, take a payment, and watch the split land in two accounts.
---
# Onboard your first seller
The fastest way to see Connect work is to onboard a test seller, take a single payment, and watch the application fee and seller payout land in the dashboard. The whole walkthrough takes about ten minutes.
{% hint style="info" %}
This walkthrough uses test mode. Test sellers, test payments, and test payouts are all real API objects — but no money moves and nothing leaves the test environment.
{% endhint %}
{% stepper %}
{% step %}
### Open the test dashboard
Sign in to <a href="https://gitbook.com"><code class="expression">space.vars.dashboard_test</code></a> and click into **Connect** in the left sidebar.
{% endstep %}
{% step %}
### Create a connected account
Go to **Connect → Connected accounts** and click **New account**. Set:
* **Account type** — `individual` (the default for the demo)
* **Country** — `United States`
* **Email** — your test email
Click **Send onboarding link**. Evolve generates a hosted onboarding URL and emails it to the address you entered.
{% endstep %}
{% step %}
### Complete onboarding as the seller
Open the onboarding email and click the link. The hosted flow walks the seller through:
1. Personal info (name, address, DOB).
2. Identity verification (test fixture passes by default).
3. A test bank account for payouts (use routing `110000000` and account `000123456789`).
Submit. The account moves to **Verified** in your dashboard within seconds.
{% endstep %}
{% step %}
### Take a payment
Back in your dashboard, find the new connected account and click **Take a test payment**. Set:
* **Amount** — `$100.00`
* **Application fee** — `$2.00` (the default 2%)
* **Card** — use the test card `4242 4242 4242 4242`
Click **Charge**. The payment succeeds. The dashboard shows two related entries:
* **Charge** of $100.00 against the buyer's card.
* **Transfer** of $98.00 to the seller's connected account.
The remaining $2.00 is your application fee.
{% endstep %}
{% step %}
### Trigger a payout
Sellers receive payouts on a schedule (daily, weekly, monthly), but you can trigger one on demand for testing. From the seller's account page, click **Pay out now**. The $98.00 lands in the test bank account immediately.
In live mode, the payout would appear in the seller's bank within 1–3 business days depending on your plan and the seller's country.
{% endstep %}
{% endstepper %}
## What happened in the dashboard
Two pages worth knowing about:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-store" style="color:$primary;">:store:</i></h3></td><td><strong>Connected accounts</strong></td><td>Every seller, with their onboarding status, payout schedule, and balance.</td><td></td></tr><tr><td><h3><i class="fa-list" style="color:$primary;">:list:</i></h3></td><td><strong>Platform feed</strong></td><td>Every charge, transfer, application fee, and payout across all sellers.</td><td></td></tr></tbody></table>
## What's next
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-toggle-on" style="color:$primary;">:toggle-on:</i></h3></td><td><strong>Switch to live mode</strong></td><td>What changes when you flip the switch.</td><td><a href="test-and-live-mode.md">test-and-live-mode.md</a></td></tr><tr><td><h3><i class="fa-window-maximize" style="color:$primary;">:window-maximize:</i></h3></td><td><strong>Add a checkout</strong></td><td>Hosted, embedded, or fully custom buyer experience.</td><td><a href="../embedded-checkout/README.md">README.md</a></td></tr><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Configure splits</strong></td><td>Application fees, flat fees, and conditional rules.</td><td><a href="../platform-setup/splitting-payments.md">splitting-payments.md</a></td></tr></tbody></table>
references/example-site/products/connect/quickstart/test-and-live-mode.md
---
icon: flask
description: Two separate environments — what changes between them, and how to flip safely.
---
# Test mode and live mode
{% include "../.gitbook/includes/environments.md" %}
## What's different in live mode for Connect
Connect has all the live-mode considerations of [Payments live mode](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/quickstart/test-and-live-mode), plus several specific to platforms:
* **Real seller onboarding.** Live connected accounts go through real [identity](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/identity-verification) and [bank](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/bank-account) verification. Onboarding can take from minutes (US individuals) to days (international businesses).
* **Real KYC obligations.** Live mode triggers your platform's KYC obligations under the regimes you operate in. See [Identity / Regional requirements](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/compliance/regional-requirements).
* **Real payouts.** Live payouts move real money to seller bank accounts on the schedule you've configured.
* **Real disputes.** Disputes opened against your sellers' charges hit the platform's dispute queue and can affect both your platform's risk profile and the seller's account standing.
## Flipping from test to live
There is no merge or migration step — test connected accounts don't carry over. Sellers go through onboarding once in test (for your integration testing) and again in live (for real).
{% stepper %}
{% step %}
### Complete platform onboarding
Your own platform account needs to be live-mode-eligible — business verification, banking, and a signed platform agreement. Most platforms do this once and then forget.
{% endstep %}
{% step %}
### Generate live keys
Under **Developers → API keys**, generate live secret and publishable keys. Restricted keys for downstream services (BI tools, internal services) work the same as in [Payments](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/quickstart/test-and-live-mode).
{% endstep %}
{% step %}
### Update onboarding URLs
Hosted onboarding URLs are environment-specific. The links your platform sends to sellers must point at the live environment, not test. If you build a custom onboarding flow, this is the URL prefix to swap.
{% endstep %}
{% step %}
### Update webhook endpoints
Live-mode webhooks are signed with a separate signing secret. Update your webhook handler to use the live secret before flipping the key.
{% endstep %}
{% endstepper %}
{% hint style="warning" %}
**Don't run test and live onboarding in parallel during cutover.** Sellers who go through test onboarding and assume they're live will be confused when they don't get paid. Make the cutover sharp — turn off test-mode self-serve onboarding before announcing live mode is open.
{% endhint %}
## Volume limits
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="layer-group" %}
**You're on Growth** — up to **100 connected accounts** in live mode. Test mode is unlimited. To go beyond 100, [upgrade to Enterprise](mailto:support@evolve.com).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="layer-group" %}
**You're on Enterprise** — unlimited connected accounts in both environments.
{% endhint %}
{% endif %}
## Cutting over checklist
Before flipping production:
<details>
<summary>Run a single live payment through one real seller</summary>
Use a colleague or your own business as the live seller. Onboard them, take a $1 payment with your real card, watch the transfer land, refund it. This proves your live keys, webhook signatures, application fees, and payout configuration are wired up correctly.
</details>
<details>
<summary>Verify the seller-facing emails point to live URLs</summary>
The onboarding email, payout notification email, and dispute alerts all contain URLs. Test-mode emails point at `dashboard.test.evolve.com`, live-mode emails point at `dashboard.evolve.com`. Make sure your email templates use the live values.
</details>
<details>
<summary>Confirm your dispute-handling plan</summary>
In live mode, disputes are real. Decide who on your team handles them, who has access to the dispute queue, and what your policy is for whether the platform absorbs disputes or passes them through to the seller. See [Refunds and disputes](../platform-setup/refunds-and-disputes.md).
</details>
references/example-site/products/connect/README.md
---
icon: circles-overlap
description: Embed payments in your platform — accept money on behalf of your sellers, take a cut, and pay them out.
cover: .gitbook/assets/connect-cover.png
coverY: 0
layout:
width: wide
cover:
visible: true
size: full
title:
visible: true
description:
visible: true
tableOfContents:
visible: true
outline:
visible: true
pagination:
visible: true
metadata:
visible: true
tags:
visible: true
---
# Connect
{% columns %}
{% column %}
Evolve Connect is the platform layer of Evolve Payments. If you operate a marketplace, a SaaS that takes payments on behalf of customers, or a vertical platform with embedded financial services, Connect handles the parts that get hard at the platform scale: onboarding sellers, splitting each payment, paying them out, and managing refunds and disputes across many accounts.
<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">Ask the Evolve docs</button>
<button type="button" class="button secondary" data-action="ask" data-query="How do I onboard my first seller?" data-icon="user-plus">First seller</button> <button type="button" class="button secondary" data-action="ask" data-query="How do application fees work?" data-icon="percent">Application fees</button> <button type="button" class="button secondary" data-action="ask" data-query="Hosted vs embedded checkout?" data-icon="window-maximize">Hosted vs embedded</button>
{% endcolumn %}
{% column %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This space demonstrates **synced blocks reused from Payments** — the test/live environments table on [Test mode and live mode](quickstart/test-and-live-mode.md) is the same block used in the Payments and Identity spaces.
{% if !visitor.claims.unsigned.persona %}
Try a persona to see adaptive content in action across the site:
<a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=new&visitor.plan=growth" class="button secondary" data-icon="seedling">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona %}
<i class="fa-id-card-clip" style="color:$info;">:id-card-clip:</i> You are currently <code class="expression">visitor.claims.unsigned.persona === "prospect" ? "a prospect user exploring the product" : visitor.claims.unsigned.persona === "new" ? "a new user" : visitor.claims.unsigned.persona === "existing" ? "an existing user" : visitor.claims.unsigned.persona === "partner" ? "a partner" : ""</code><code class="expression">visitor.claims.unsigned.plan ? " on the " + visitor.claims.unsigned.plan.charAt(0).toUpperCase() + visitor.claims.unsigned.plan.slice(1) + " plan" : ""</code>. [<mark style="color:$primary;">Reset</mark>](https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=)
{% endif %}
{% if visitor.claims.unsigned.persona === "prospect" %}
<a class="button primary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=new&visitor.plan=growth" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a class="button primary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=new&visitor.plan=growth" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a class="button primary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=new&visitor.plan=growth" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/connect?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a class="button primary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% endhint %}
{% endcolumn %}
{% endcolumns %}
***
## <i class="fa-sparkle" style="color:$info;">:sparkle:</i> Picked for you
{% if visitor.claims.unsigned.persona === "prospect" %}
{% hint style="info" icon="store" %}
**Evaluating Connect for your platform?** The basics of how the buyer/seller/platform money flow works.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-circles-overlap" style="color:$primary;">:circles-overlap:</i></h3></td><td><h3><strong>How Connect fits</strong></h3></td><td>Buyer, seller, platform — and the money flow between them.</td><td><a href="README.md">connect</a></td></tr><tr><td><h3><i class="fa-percent" style="color:$primary;">:percent:</i></h3></td><td><strong>Splitting payments</strong></td><td>Application fees, flat fees, conditional rules.</td><td><a href="platform-setup/splitting-payments.md">splitting-payments</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
{% hint style="info" icon="hand-wave" %}
**New to Connect?** Onboard your first test seller in five minutes.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><h3><strong>Onboard your first seller</strong></h3></td><td>End-to-end walkthrough — connected account, hosted onboarding, first transfer.</td><td><a href="quickstart/onboard-your-first-seller.md">onboard-your-first-seller</a></td></tr><tr><td><h3><i class="fa-window-maximize" style="color:$primary;">:window-maximize:</i></h3></td><td><strong>Pick a checkout shape</strong></td><td>Hosted, embedded, or fully custom buyer experience.</td><td><a href="embedded-checkout/hosted-vs-embedded.md">hosted-vs-embedded</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
{% hint style="info" icon="arrows-left-right" %}
**Coming from Stripe Connect?** Connected accounts, application fees, and transfers all work the same. Account types are unified.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-arrows-left-right" style="color:$primary;">:arrows-left-right:</i></h3></td><td><h3><strong>Migrate from Stripe</strong></h3></td><td>The full cutover plan, including Connect-specifics.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/migrate-from-stripe">migrate-from-stripe</a></td></tr><tr><td><h3><i class="fa-user-plus" style="color:$primary;">:user-plus:</i></h3></td><td><strong>Onboarding sellers</strong></td><td>The unified account model that replaces Express/Standard/Custom.</td><td><a href="platform-setup/onboarding-sellers.md">onboarding-sellers</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
{% hint style="info" icon="building" %}
**Building a custom platform?** White-label embedded checkout, custom seller onboarding, consolidated KYC are Enterprise capabilities.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><h3><strong>Custom Connect onboarding</strong></h3></td><td>Programmatic onboarding for teams that want their own UX.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/marketplace/custom-onboarding">custom-onboarding</a></td></tr><tr><td><h3><i class="fa-paint-roller" style="color:$primary;">:paint-roller:</i></h3></td><td><strong>White-label checkout</strong></td><td>Per-seller domains, fonts, custom CSS for premium sellers.</td><td><a href="embedded-checkout/customization.md">customization</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona %}
***
{% endif %}
{% if !visitor.claims.unsigned.persona %}
## Get started
{% endif %}
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><strong>Quickstart</strong></td><td>Onboard your first seller in five minutes.</td><td><a href="quickstart/onboard-your-first-seller.md">onboard-your-first-seller</a></td></tr><tr><td><h3><i class="fa-window-maximize" style="color:$primary;">:window-maximize:</i></h3></td><td><strong>Embedded checkout</strong></td><td>The buyer-facing flow — hosted, embedded, or fully custom.</td><td><a href="embedded-checkout/README.md">embedded-checkout</a></td></tr><tr><td><h3><i class="fa-sliders" style="color:$primary;">:sliders:</i></h3></td><td><strong>Platform setup</strong></td><td>Onboarding, splitting, payouts, and dispute handling.</td><td><a href="platform-setup/README.md">platform-setup</a></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>API reference</strong></td><td>Endpoints, SDKs, and try-it.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/connect-api/">connect-api</a></td></tr></tbody></table>
## Where Connect fits
A typical Connect transaction touches three parties — the **buyer**, the **seller** (your customer), and **you** (the platform). Connect orchestrates the money so each one ends up with the right amount.
{% columns %}
{% column width="33%" %}
### <i class="fa-cart-shopping" style="color:$primary;">:cart-shopping:</i> Buyer
Pays once, sees one charge on their statement (yours or the seller's, your choice). Refunds and support flow back through your platform.
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-store" style="color:$primary;">:store:</i> Seller
Onboarded once via your platform, then receives payouts on a schedule you set. Their identity, banking, and tax records are handled by [Identity](https://enterprise-demos.gitbook.io/evolve-docs/identity).
{% endcolumn %}
{% column width="33%" %}
### <i class="fa-circles-overlap" style="color:$primary;">:circles-overlap:</i> Platform
Takes a percentage of each transaction as an application fee, plus optional flat fees. Sees consolidated reporting across all sellers in one dashboard.
{% endcolumn %}
{% endcolumns %}
## Get help
{% columns %}
{% column width="50%" %}
#### Talk to support
For account-specific questions, billing, or production incidents, contact your account team or open a ticket from the dashboard.
<a href="https://gitbook.com" class="button primary">Open a ticket</a>
{% endcolumn %}
{% column width="50%" %}
#### Search the docs
Looking for something specific? The Assistant pulls answers from this site, the API reference, and the community forum.
<button type="button" class="button secondary" data-action="search" data-icon="magnifying-glass">Search...</button>
{% endcolumn %}
{% endcolumns %}
references/example-site/products/connect/SUMMARY.md
# Table of contents
* [Connect](README.md)
## Quickstart
* [Onboard your first seller](quickstart/onboard-your-first-seller.md)
* [Test mode and live mode](quickstart/test-and-live-mode.md)
## Embedded checkout
* [Overview](embedded-checkout/README.md)
* [Buyer experience](embedded-checkout/buyer-experience.md)
* [Hosted vs embedded](embedded-checkout/hosted-vs-embedded.md)
* [Customization](embedded-checkout/customization.md)
## Platform setup
* [Overview](platform-setup/README.md)
* [Onboarding sellers](platform-setup/onboarding-sellers.md)
* [Splitting payments](platform-setup/splitting-payments.md)
* [Payouts to sellers](platform-setup/payouts.md)
* [Refunds and disputes](platform-setup/refunds-and-disputes.md)
references/example-site/products/identity/.gitbook/includes/environments.md
---
title: Test and live environments
---
Evolve has two fully separate environments. They share no data — keys, customers, charges, and webhooks all exist independently in each.
| Environment | API base URL | Dashboard | Key prefix |
| --- | --- | --- | --- |
| Test | <code class="expression">space.vars.api_test</code> | <code class="expression">space.vars.dashboard_test</code> | `sk_test_` / `pk_test_` |
| Live | <code class="expression">space.vars.api_live</code> | <code class="expression">space.vars.dashboard_live</code> | `sk_live_` / `pk_live_` |
Test mode accepts only test fixture data — no real cards, real documents, or real bank accounts. Nothing leaves the test environment.
references/example-site/products/identity/.gitbook/vars.yaml
api_live: https://api.evolve.com
api_test: https://api.test.evolve.com
dashboard_live: https://dashboard.evolve.com
dashboard_test: https://dashboard.test.evolve.com
support_email: support@evolve.com
status_page: https://status.evolve.com
verification_ttl_days: 30
document_supported_countries: 195
references/example-site/products/identity/compliance/audit-logs.md
---
icon: clipboard-list
description: Every verification, decision, and override is logged — searchable, exportable, immutable.
---
# Audit logs
Every action Evolve takes on your behalf — and every action your team takes in the dashboard — is recorded in the audit log. It's the artifact your auditor, your bank, or your regulator will ask for first when something needs to be explained.
Logs are append-only. You can't edit or delete an entry; even Evolve's own staff can't.
## What's logged
Every entry includes the **what**, the **who**, the **when**, and the **why**. The full schema:
| Field | Description | Example |
| --- | --- | --- |
| `event` | The action taken | `verification.completed` |
| `actor_type` | What kind of entity acted | `evolve_system`, `team_member`, `customer`, `api_client` |
| `actor_id` | Who exactly | `user_3K2pL9q`, `cust_a8N3mF` |
| `subject_type` | What was acted on | `verification_session`, `customer`, `business` |
| `subject_id` | Specifically | `vs_3KsM12pL9qXa7` |
| `timestamp` | When | `2026-04-30T14:22:01.341Z` |
| `metadata` | Action-specific details | `{ "reason": "manual_override", "note": "false positive..." }` |
| `request_id` | The request that triggered the entry | `req_8h2nF6m4Lp` |
## Categories of event
The events that show up most often, grouped by what they're about:
<details>
<summary>Verification events</summary>
* `verification.created` — a new session was created (by API or dashboard)
* `verification.completed` — final decision reached
* `verification.failed` — explicit failure, with reason
* `verification.manually_reviewed` — a human reviewer made the call
* `verification.overridden` — a team member changed the automated decision
* `verification.expired` — the session timed out before completion
</details>
<details>
<summary>Document events</summary>
* `document.uploaded`
* `document.reviewed`
* `document.flagged_for_tampering`
* `document.deleted` — per retention policy or manual deletion
</details>
<details>
<summary>Screening events</summary>
* `screening.run`
* `screening.match_found`
* `screening.match_overridden` — false-positive override, with required reason
* `screening.match_added` — ongoing monitoring detected a new match
</details>
<details>
<summary>Configuration events</summary>
* `settings.changed` — any change to a workspace setting
* `team.member_added` / `team.member_removed`
* `permissions.changed`
* `api_key.created` / `api_key.revoked` / `api_key.rolled`
</details>
<details>
<summary>Data access events</summary>
* `pii.accessed` — when a team member views a verification's PII
* `pii.exported` — when PII is included in a CSV or report download
* `pii.deleted` — manual or scheduled deletion
</details>
## Searching the log
In **Compliance → Audit log**, you can filter by:
* **Date range** (down to the second)
* **Event type** (any of the events above)
* **Actor** (specific team member, system, or customer)
* **Subject** (a specific verification, customer, or business)
* **Free text** in the metadata
The default view shows the last 30 days; you can go back as far as your retention policy allows.
## Exporting
Three ways to get the log out:
* **CSV download** from the dashboard, with current filters applied.
* **Scheduled export** to SFTP, S3, or your data warehouse — same destinations as [Payments reporting](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reporting/sharing-exports).
* **API pull** for real-time integration into a SIEM (Splunk, Datadog, etc.).
For SOC 2 and ISO 27001 audit support, the standard scheduled export — full log, daily, in CSV, to your auditor's SFTP — is the most common setup.
## Retention
Audit log entries are retained for **7 years by default** — long enough for most regulatory regimes (BSA/AML in the US is 5 years; many EU regimes are 5–10 years). You can configure a longer retention if you need it; you can't configure a shorter one for the action log itself, since that would defeat the audit trail's purpose.
PII referenced in the log (specific document images, selfie images) follows your [data retention policy](data-retention.md), separately from the log entry itself. After PII is purged, log entries that referenced it remain — they just point to a "purged per retention policy" placeholder instead of the original data.
## Tamper-evidence
The log is append-only and hashed: every entry's hash includes the previous entry's hash, forming a Merkle-style chain. Evolve's internal infrastructure cannot insert a backdated entry without breaking the chain.
For Enterprise customers, the daily root hash is published to a public timestamp service, so you can prove the log existed in its current form at a specific date. This is overkill for most teams but matters for highly regulated verticals.
## Permissions
Audit log access is gated by role:
| Role | Can see |
| --- | --- |
| Viewer | Aggregate counts only |
| Compliance | Full log, including PII access events |
| Admin | Full log, plus permission to schedule exports |
The dashboard surfaces "who looked at what" — every PII view is itself an audit log entry, visible to your compliance team.
## Related
* [Data retention](data-retention.md) — how long PII is kept; logs persist longer.
* [Regional requirements](regional-requirements.md) — what regulators require to be in the log.
* [Payments / Reporting](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reporting/sharing-exports) — schedule the same kind of export.
references/example-site/products/identity/compliance/data-retention.md
---
icon: clock-rotate-left
description: How long Evolve keeps verification PII, why, and how to change the policy.
---
# Data retention
Identity verification produces sensitive PII — government ID images, selfies, bank account numbers, beneficial owner records. The shorter you can hold it, the smaller your privacy footprint. The longer you hold it, the easier it is to satisfy auditors and regulators when something gets questioned later.
The right retention window is a balance between the two, tuned to the regulatory regime you operate in. Evolve gives you the controls; the policy is yours to set.
## Default retention
Out of the box, Evolve retains verification PII as follows:
| Data type | Default retention |
| --- | --- |
| Document images (front and back) | <code class="expression">space.vars.verification_ttl_days</code> days |
| Selfie images | <code class="expression">space.vars.verification_ttl_days</code> days |
| Extracted document data (name, DOB, document number) | 7 years |
| Selfie liveness scores | 7 years |
| Sanctions and PEP screening results | 7 years |
| Bank account numbers (encrypted) | 7 years |
| Audit log entries | 7 years (see [Audit logs](audit-logs.md)) |
The pattern: the **raw images** purge quickly (default 30 days), the **extracted data and decisions** stick around for the regulatory floor.
## Why these defaults
* **30-day raw images** is short enough to minimize PII exposure but long enough that customer-support edge cases can still review the original capture if a verification is contested.
* **7-year extracted data** matches the BSA/AML floor in the US and most EU regimes. It means a regulator asking "show me the verifications you ran in 2023" will have the metadata to answer, even though the images themselves are long gone.
* **7-year audit logs** is the same regulatory floor — and protects your team's ability to demonstrate due process if a decision is later questioned.
## Configuring retention
In **Settings → Identity → Retention**, you can set per-data-type retention windows:
| Setting | Range | Default |
| --- | --- | --- |
| Document images | 1 day to 7 years | 30 days |
| Selfie images | 1 day to 7 years | 30 days |
| Extracted data | 1 year to 10 years | 7 years |
| Audit logs | 7 years (locked) | 7 years |
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="info" %}
**Enterprise-specific:** you can also configure per-region retention to match local rules (e.g. shorter retention for EU residents under GDPR, longer for regulated US verticals). Set these under **Settings → Identity → Regional retention**.
{% endhint %}
{% endif %}
Changes apply going forward; existing data is purged on its existing schedule unless you trigger a one-time backfill.
## Customer deletion requests
GDPR, CCPA, and most modern privacy regimes give individuals the right to request deletion of their data. Evolve supports this:
{% stepper %}
{% step %}
### Customer requests deletion
The customer asks you (via your support channel or a privacy portal you operate) to delete their data.
{% endstep %}
{% step %}
### You issue the deletion
In the dashboard, find the verification or customer and click **Delete data**. You can also issue deletions in bulk via the API for privacy-portal automations.
{% endstep %}
{% step %}
### Evolve purges within 30 days
PII is purged from active and backup systems within 30 days. The audit log retains the deletion event itself, but the PII it references becomes a placeholder.
{% endstep %}
{% endstepper %}
{% hint style="warning" %}
**Some data can't be deleted on request.** If a verification is required by law to be retained (e.g. a sanctions match that triggered a regulatory filing), you cannot delete it — and the customer can't compel you to. The dashboard surfaces these "cannot delete" cases with the legal basis for retention.
{% endhint %}
## What gets purged when
When the retention window for a data type expires, Evolve's purge process runs nightly to:
1. **Identify expired data** — anything past its retention window.
2. **Anonymize references** — log entries pointing to the data update to "purged per retention policy."
3. **Delete from active systems** — the encrypted blob is removed from primary storage.
4. **Delete from backups** — backups containing the data are themselves purged on a 90-day rolling window. Until that, the data is recoverable only by Evolve operations staff under documented break-glass procedures.
The dashboard's **Compliance → Retention** page shows what's been purged in the last 90 days, so you can see the policy operating.
## Encryption at rest
While data is retained, it's encrypted with per-tenant keys (KMS-managed). Even within Evolve, document images and PII are not accessible without specific authorization tied to a request id. Evolve operations staff cannot routinely browse customer data; access is by exception, logged, and time-limited.
For Enterprise customers, you can supply your own KMS keys (BYOK) so encryption depends on a key you control. This is configured in **Settings → Security → Encryption keys**.
## Related
* [Audit logs](audit-logs.md) — what's kept indefinitely (or near-indefinitely) for accountability.
* [Regional requirements](regional-requirements.md) — region-specific retention floors and ceilings.
* [Identity verification](../verification-flows/identity-verification/README.md) — the source of most retained data.
references/example-site/products/identity/compliance/regional-requirements.md
---
icon: globe
description: KYC, AML, and privacy obligations that vary by where your customers are.
---
# Regional requirements
The regulations that govern identity verification vary widely by country, and sometimes by state or province. Evolve is built to satisfy the major regimes — but the obligation to comply sits with you, not us. This page summarizes the rules that most affect Evolve customers and points to the specific Evolve features that help you satisfy them.
{% hint style="info" %}
**This page is a summary, not legal advice.** Talk to qualified counsel before launching in a new jurisdiction. The specifics of what you need to verify, retain, and report depend on your business model and customer base.
{% endhint %}
## United States
The federal floor is set by the Bank Secrecy Act (BSA) and Customer Identification Program (CIP) rules. State-level rules layer on top.
### Customer Identification Program (CIP)
For financial institutions and money-transmitter-licensed businesses, CIP requires you to:
* Collect **name, date of birth, address, and a government identifier** (SSN, ITIN, or passport number for non-residents).
* Verify the identity using documents, non-documentary methods, or both.
* **Retain records for 5 years** after the customer relationship ends.
Evolve's identity verification flow collects all the required fields and Evolve retains the verification record per your [data retention](data-retention.md) policy (default 7 years, configurable down to 5).
### OFAC
OFAC (Office of Foreign Assets Control) maintains the SDN list and other US sanctions programs. You must screen customers and counterparties against these lists. Evolve's [watchlist screening](../verification-flows/identity-verification/watchlist-screening.md) and [sanctions screening](../verification-flows/business/sanctions-screening.md) cover the OFAC lists by default.
### State-level
* **California** — CCPA and CPRA give consumers privacy rights including deletion. Evolve supports per-customer deletion (see [Data retention](data-retention.md#customer-deletion-requests)).
* **New York** — NY DFS Part 500 (cybersecurity rules) requires logged, audit-able access to PII. Evolve's [audit logs](audit-logs.md) cover this.
## European Union and EEA
The EU has the broadest set of obligations of any major region.
### GDPR
The General Data Protection Regulation imposes:
* A **lawful basis** for processing PII — typically "performance of a contract" or "compliance with legal obligation" for verification.
* **Data minimization** — only collect what you need. Don't run KYB if all you need is identity verification.
* **Right to access, correction, and erasure** for individuals.
* **Breach notification** within 72 hours.
* **Transfers outside the EU** require an adequacy decision or Standard Contractual Clauses.
Evolve is GDPR-compliant by default. Specific helps:
* Per-region retention policies (configurable to GDPR-aligned shorter windows).
* Per-customer deletion via dashboard or API.
* Data residency in EU regions for Enterprise customers.
### AMLD6
The Sixth Anti-Money-Laundering Directive sets the EU's KYC/KYB floor. Most of it overlaps with US BSA/CIP, with two notable additions:
* **Beneficial ownership at 25% or more** — same as US, but the EU verifies against national registers.
* **Adverse media checks** are explicitly required for higher-risk customers.
Evolve's [beneficial ownership](../verification-flows/business/beneficial-ownership.md) flow handles the 25% rule, and [sanctions screening](../verification-flows/business/sanctions-screening.md) includes EU adverse-media sources.
### eIDAS
For high-value transactions in the EU, eIDAS-compliant electronic identity verification is required. Evolve's identity verification flow can produce an **eIDAS-compatible audit trail** when configured for that mode (Settings → Identity → eIDAS mode).
## United Kingdom
UK rules largely mirror EU AMLD6, with UK-specific lists:
* **HMRC and JMLSG guidance** for AML compliance.
* **HMT consolidated list** for sanctions (in addition to UN, EU, OFAC).
* **Data Protection Act 2018** — UK's GDPR-equivalent, with one notable difference: shorter retention for some categories.
Evolve's UK setup uses HMT alongside the standard sanctions sources, and applies UK-specific retention defaults when you set the workspace region to UK.
## Other regions
Brief notes on regions Evolve supports today:
<details>
<summary>Canada</summary>
PIPEDA (federal privacy), provincial privacy regimes (notably Quebec's Law 25), and FINTRAC's PCMLTFA for AML. Evolve supports Canadian provincial driver's licenses and provincial ID cards in identity verification, and screens against the Canadian Consolidated Sanctions list.
</details>
<details>
<summary>Australia</summary>
Privacy Act 1988 (federal) and AUSTRAC's AML/CTF rules. Australian passports, driver's licenses, and Medicare cards are supported in identity verification. AUSTRAC screening lists are included in Enterprise sanctions screening.
</details>
<details>
<summary>Singapore</summary>
PDPA (privacy) and MAS (financial services). Singapore IC and FIN cards supported. MyInfo integration available for instant data prefill (Enterprise only).
</details>
<details>
<summary>India</summary>
Aadhaar verification supported via Evolve's UIDAI partnership (Enterprise only, for customers with the appropriate license). RBI KYC rules apply for financial-services customers.
</details>
<details>
<summary>Other countries</summary>
For countries not listed here, identity verification works (passports are universal), but country-specific document types may not all be supported. The full list is in **Settings → Identity → Supported documents**.
</details>
## Re-verification cadence
Most regimes don't require re-verification on a fixed cadence, but several recommend it. Common patterns:
| Regime | Recommended re-verification |
| --- | --- |
| US (BSA / CIP) | On material customer-relationship change (e.g. address change, large transaction). |
| EU (AMLD6) | Every 1–3 years for higher-risk customers; on trigger event. |
| UK (HMRC) | Every 1–3 years; risk-based. |
| Canada (PCMLTFA) | Every 2 years for high-risk; on trigger event. |
Evolve's re-verification API lets you trigger a fresh flow against an existing customer at any time. Combined with [audit logs](audit-logs.md), this gives you the cadence your regime requires plus the documented record of having done it.
## Related
* [Audit logs](audit-logs.md) — the trail your auditor needs.
* [Data retention](data-retention.md) — region-specific retention configuration.
* [Beneficial ownership](../verification-flows/business/beneficial-ownership.md) — the 25% rule under FinCEN, EU AMLD6, and UK MLRs.
* [Watchlist screening](../verification-flows/identity-verification/watchlist-screening.md) — OFAC, UN, EU, UK lists.
references/example-site/products/identity/quickstart/run-your-first-verification.md
---
icon: rocket
description: Run your first identity verification from the dashboard — no integration required.
---
# Run your first verification
The fastest way to see Identity work is to send a verification link, complete it yourself with test fixture data, and watch the result land in your dashboard. You don't need to write any code.
{% hint style="info" %}
This walkthrough uses test mode. Test verifications are real, but no documents are stored long-term and nothing leaves the test environment.
{% endhint %}
{% stepper %}
{% step %}
### Open the test dashboard
Sign in to <a href="https://gitbook.com"><code class="expression">space.vars.dashboard_test</code></a> and click into **Identity** in the left sidebar.
{% endstep %}
{% step %}
### Create a verification session
Go to **Identity → Verification sessions** and click **New session**. Set:
* **Type** — `identity` (the default)
* **Required checks** — leave defaults (document + selfie)
* **Subject** — your test email address
Click **Create**. Evolve generates a hosted verification URL — copy it.
{% endstep %}
{% step %}
### Complete the verification
Open the link in a new tab on a phone (the camera capture flow is mobile-first). On the hosted flow:
1. Pick a country and document type — choose **United States → Driver's license**.
2. Use the test image **`test-dl-front.jpg`** when asked to capture the front. Test images are linked at the bottom of the test-mode capture screen.
3. Use **`test-dl-back.jpg`** for the back.
4. For the selfie, point the camera at any face — test mode accepts any image.
{% endstep %}
{% step %}
### See it in the dashboard
Back in the dashboard, the new verification appears in **Identity → All sessions** with a status of **Verified**. Click it to see the full timeline — when each check started, what passed, what flagged, and the final decision.
{% endstep %}
{% step %}
### See a failure
The default test fixture passes everything. To see a failure, repeat the flow and use **`test-dl-expired.jpg`** instead of `test-dl-front.jpg`. The verification now ends with status **Failed** and reason `document_expired`.
{% endstep %}
{% endstepper %}
## What's next?
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-toggle-on" style="color:$primary;">:toggle-on:</i></h3></td><td><strong>Switch to live mode</strong></td><td>What changes when you flip the switch.</td><td><a href="test-and-live-mode.md">test-and-live-mode.md</a></td></tr><tr><td><h3><i class="fa-list-check" style="color:$primary;">:list-check:</i></h3></td><td><strong>Pick a flow</strong></td><td>Identity, bank, business — when to use which.</td><td><a href="../verification-flows/README.md">README.md</a></td></tr><tr><td><h3><i class="fa-bolt" style="color:$primary;">:bolt:</i></h3></td><td><strong>Set up a webhook</strong></td><td>React to verification results automatically.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks">README.md</a></td></tr></tbody></table>
{% hint style="info" %}
**Building an integration?** Developers / [Identity API quickstart](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/identity-api) covers the same flow with code samples in cURL, Node, Python, Go, and Ruby.
<p><button type="button" class="button primary" data-action="ask" data-query="How do I integrate Evolve Identity with my own onboarding flow?" data-icon="code">Ask the docs</button></p>
{% endhint %}
references/example-site/products/identity/quickstart/test-and-live-mode.md
---
icon: flask
description: Two separate environments — what changes between them, and how to flip safely.
---
# Test mode and live mode
{% include "../.gitbook/includes/environments.md" %}
## What's different in live mode
* **Real documents are reviewed.** Verifications hit the actual document database, the selfie liveness model, and (where enabled) the watchlist screening engine. Test mode uses fixture results.
* **PII is encrypted and stored.** Test mode discards documents and selfies on session completion. Live mode encrypts and retains them per your [data retention](../compliance/data-retention.md) policy.
* **Per-verification fees apply.** Test mode is free. See [fees](#fees) below.
* **Webhooks fire to your live endpoint.** Make sure your verification handler is using the live signing secret (`whsec_live_*`).
## Flipping from test to live
There is no merge or migration step — test and live are fully separate.
1. Complete the dashboard onboarding (business verification, banking, retention policy review).
2. Generate a live key under **Developers → API keys**.
3. Swap `sk_test_*` for `sk_live_*` in your environment configuration.
4. Update webhook endpoints to point at your production URL and use the live signing secret.
{% hint style="warning" %}
**Live mode triggers regulatory obligations.** Once you're processing real verifications, you're handling PII in scope of GDPR, CCPA, and (where applicable) regional KYC rules. Make sure your privacy notice and retention policy reflect what Evolve does on your behalf — see [Regional requirements](../compliance/regional-requirements.md).
{% endhint %}
## Fees
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="info" icon="layer-group" %}
**You're on Starter** — identity verifications cost **$1.50 each**. Bank verification, KYB, and watchlist screening aren't available on Starter.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="layer-group" %}
**You're on Growth** — identity verifications cost **$1.20 each**. Bank verification (Plaid) is **$2.50**, micro-deposits are **$0.80**.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="layer-group" %}
**You're on Enterprise** — pricing is per your contract. The standard published rates below show defaults; your effective rates are visible in **Settings → Billing**.
{% endhint %}
{% endif %}
| Verification | Starter | Growth | Enterprise |
| --- | --- | --- | --- |
| Identity (document + selfie) | $1.50 | $1.20 | Custom |
| Bank account (Plaid instant) | — | $2.50 | Custom |
| Bank account (micro-deposits) | — | $0.80 | Custom |
| Business verification (KYB) | — | — | $5.00 / Custom |
| Watchlist screening (initial) | — | — | $0.50 |
| Watchlist screening (re-screen) | — | — | $0.10 |
Verification fees appear on your settlement file as a separate line type `verification_fee`, alongside payment processing fees from [Payments](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reconciliation/settlement-files).
## Cutting over from test
Before flipping the production switch, the team checks we recommend:
<details>
<summary>Run one real verification end to end</summary>
Use a colleague with consent and a real ID. Confirm the result lands in the dashboard, the webhook fires, and the data appears in your downstream system.
</details>
<details>
<summary>Verify your privacy notice covers Identity</summary>
GDPR and CCPA require you to disclose that an automated decisioning service processes ID documents on your behalf. The standard wording is in [Regional requirements → GDPR](../compliance/regional-requirements.md#gdpr).
</details>
<details>
<summary>Set your retention window</summary>
The default is 30 days post-verification. If you need longer (compliance) or shorter (data minimization), change it in **Settings → Identity → Retention** before going live. See [Data retention](../compliance/data-retention.md).
</details>
references/example-site/products/identity/README.md
---
icon: id-card
description: Verify customers and partners — documents, selfies, bank accounts, and business records.
cover: .gitbook/assets/identity-cover.png
coverY: 0
layout:
width: wide
cover:
visible: true
size: full
title:
visible: true
description:
visible: true
tableOfContents:
visible: true
outline:
visible: true
pagination:
visible: true
metadata:
visible: true
tags:
visible: true
---
# Identity
{% columns %}
{% column %}
Evolve Identity verifies the people and businesses you transact with — through document review, biometric selfie checks, bank account verification, and business records (KYB). It's the same platform as Payments, with the same dashboard, the same auth, and the same settlement of fees.
<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">Ask the Evolve docs</button>
<button type="button" class="button secondary" data-action="ask" data-query="Which verification flow should I use?" data-icon="route">Which flow?</button> <button type="button" class="button secondary" data-action="ask" data-query="What documents are accepted in each country?" data-icon="passport">Documents</button> <button type="button" class="button secondary" data-action="ask" data-query="How do I screen against watchlists?" data-icon="shield-halved">Screening</button>
{% endcolumn %}
{% column %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This space demonstrates **nested page groups** — Verification flows has parent pages (Identity, Bank, Business) each with their own subpages. It also includes a **hidden page**: [Watchlist screening](verification-flows/identity-verification/watchlist-screening.md) doesn't appear in the sidebar nav but is accessible via direct link.
{% if !visitor.claims.unsigned.persona %}
Try a persona to see adaptive content in action across the site:
<a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="seedling">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona %}
<i class="fa-id-card-clip" style="color:$info;">:id-card-clip:</i> You are currently <code class="expression">visitor.claims.unsigned.persona === "prospect" ? "a prospect user exploring the product" : visitor.claims.unsigned.persona === "new" ? "a new user" : visitor.claims.unsigned.persona === "existing" ? "an existing user" : visitor.claims.unsigned.persona === "partner" ? "a partner" : ""</code><code class="expression">visitor.claims.unsigned.plan ? " on the " + visitor.claims.unsigned.plan.charAt(0).toUpperCase() + visitor.claims.unsigned.plan.slice(1) + " plan" : ""</code>. [<mark style="color:$primary;">Reset</mark>](https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=)
{% endif %}
{% if visitor.claims.unsigned.persona === "prospect" %}
<a class="button primary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a class="button primary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a class="button primary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/identity?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a class="button primary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% endhint %}
{% endcolumn %}
{% endcolumns %}
***
## <i class="fa-sparkle" style="color:$info;">:sparkle:</i> Picked for you
{% if visitor.claims.unsigned.persona === "prospect" %}
{% hint style="info" icon="store" %}
**Evaluating Evolve Identity?** Get an overview of how the verification flows fit together.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-list-check" style="color:$primary;">:list-check:</i></h3></td><td><h3><strong>Verification flows</strong></h3></td><td>Identity, bank, and business verification — when to use which.</td><td><a href="verification-flows/README.md">verification-flows</a></td></tr><tr><td><h3><i class="fa-receipt" style="color:$primary;">:receipt:</i></h3></td><td><h3><strong>Per-verification pricing</strong></h3></td><td>What each flow costs across Starter, Growth, and Enterprise.</td><td><a href="quickstart/test-and-live-mode.md">test-and-live-mode</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
{% hint style="info" icon="hand-wave" %}
**New to Identity?** Run a verification in five minutes — no integration required.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><h3><strong>Run your first verification</strong></h3></td><td>Send a verification link, complete it with test data, see the result land.</td><td><a href="quickstart/run-your-first-verification.md">run-your-first-verification</a></td></tr><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><strong>Identity verification flow</strong></td><td>Document + selfie liveness, configurable strictness.</td><td><a href="verification-flows/identity-verification/README.md">identity-verification</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
{% hint style="info" icon="arrows-left-right" %}
**Coming from Stripe Identity?** Most flows map directly — document + selfie, bank verification, and watchlist screening.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-arrows-left-right" style="color:$primary;">:arrows-left-right:</i></h3></td><td><h3><strong>Migrate from Stripe Identity</strong></h3></td><td>Field mapping, parallel-run pattern, and the cutover checklist.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/migrate-from-stripe">migrate-from-stripe</a></td></tr><tr><td><h3><i class="fa-building-columns" style="color:$primary;">:building-columns:</i></h3></td><td><strong>Bank verification with Plaid</strong></td><td>Instant verification with micro-deposit fallback.</td><td><a href="verification-flows/bank-account/plaid-instant.md">plaid-instant</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
{% hint style="info" icon="building" %}
**Setting up enterprise compliance?** Watchlist screening, KYB, and ongoing monitoring are the Enterprise-tier capabilities.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-briefcase" style="color:$primary;">:briefcase:</i></h3></td><td><h3><strong>Business verification (KYB)</strong></h3></td><td>Beneficial ownership, sanctions screening, ongoing monitoring.</td><td><a href="verification-flows/business/README.md">business</a></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>Watchlist screening</strong></td><td>OFAC, UN, EU, UK HMT, PEP, and adverse media. Enterprise only.</td><td><a href="verification-flows/identity-verification/watchlist-screening.md">watchlist-screening</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona %}
***
{% endif %}
{% if !visitor.claims.unsigned.persona %}
## Get started
{% endif %}
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><strong>Quickstart</strong></td><td>Run your first verification in five minutes.</td><td><a href="quickstart/run-your-first-verification.md">run-your-first-verification</a></td></tr><tr><td><h3><i class="fa-list-check" style="color:$primary;">:list-check:</i></h3></td><td><strong>Verification flows</strong></td><td>Identity, bank, and business verification — when to use which.</td><td><a href="verification-flows/README.md">verification-flows</a></td></tr><tr><td><h3><i class="fa-scale-balanced" style="color:$primary;">:scale-balanced:</i></h3></td><td><strong>Compliance</strong></td><td>Audit logs, retention, regional requirements.</td><td><a href="compliance/audit-logs.md">audit-logs</a></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>API reference</strong></td><td>Endpoints, SDKs, and try-it.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/identity-api/">identity-api</a></td></tr></tbody></table>
## What's new
* **Live document capture in 195 countries** — every country except the dozen on the OFAC blocklist. [Read more](verification-flows/identity-verification/document-review.md).
* **Selfie liveness 2.0** — passive liveness, no head turns required. Lifts completion rates by ~12%. [Read more](verification-flows/identity-verification/selfie-and-liveness.md).
* **Plaid instant for businesses** — same one-tap flow, now for business bank accounts. [Read more](verification-flows/bank-account/plaid-instant.md).
## Get help
{% columns %}
{% column width="50%" %}
#### Talk to support
For account-specific questions, compliance reviews, or production incidents, contact your account team or open a ticket from the dashboard.
<a href="https://gitbook.com" class="button primary">Open a ticket</a>
{% endcolumn %}
{% column width="50%" %}
#### Search the docs
Looking for something specific? The Assistant pulls answers from this site, the API reference, and the community forum.
<button type="button" class="button secondary" data-action="search" data-icon="magnifying-glass">Search...</button>
{% endcolumn %}
{% endcolumns %}
references/example-site/products/identity/SUMMARY.md
# Table of contents
* [Identity](README.md)
## Quickstart
* [Run your first verification](quickstart/run-your-first-verification.md)
* [Test mode and live mode](quickstart/test-and-live-mode.md)
## Verification flows
* [Overview](verification-flows/README.md)
* [Identity verification](verification-flows/identity-verification/README.md)
* [Document review](verification-flows/identity-verification/document-review.md)
* [Selfie and liveness](verification-flows/identity-verification/selfie-and-liveness.md)
* [Watchlist screening](verification-flows/identity-verification/watchlist-screening.md)
* [Bank account verification](verification-flows/bank-account/README.md)
* [Plaid instant](verification-flows/bank-account/plaid-instant.md)
* [Micro-deposits](verification-flows/bank-account/micro-deposits.md)
* [Business verification (KYB)](verification-flows/business/README.md)
* [Beneficial ownership](verification-flows/business/beneficial-ownership.md)
* [Sanctions screening](verification-flows/business/sanctions-screening.md)
## Compliance
* [Audit logs](compliance/audit-logs.md)
* [Data retention](compliance/data-retention.md)
* [Regional requirements](compliance/regional-requirements.md)
references/example-site/products/identity/verification-flows/bank-account/micro-deposits.md
---
icon: coins
description: Confirm bank account ownership with two small test deposits the customer reports back.
---
# Micro-deposits
Micro-deposits work for any US bank account, including the ~30% that aren't covered by Plaid. The trade-off is speed — they take 1–2 business days to land, and the customer has to come back to confirm the amounts they received.
Use them as a fallback for [Plaid instant](plaid-instant.md), or as your primary method if you serve customers at smaller banks and credit unions.
## How it works
```mermaid
flowchart LR
A[Customer enters<br>routing + account] --> B[Two small deposits<br>sent to the account]
B --> C[Customer checks<br>their bank statement]
C --> D[Customer enters<br>the two amounts]
D --> E{Match?}
E -->|Yes| F[Verified]
E -->|No| G[Failed]
```
## What the customer experiences
It's a two-step flow with a 1–2 day gap in between:
{% stepper %}
{% step %}
### Day 1: Submit account details
The customer enters their routing number and account number on your verification flow. Evolve confirms the routing number is valid and the account exists, then schedules two small deposits — typically $0.01 to $0.99 each — to land in the account.
The customer is told to come back within 7 days, and an email reminder fires after 2 and 5 days if they haven't returned.
{% endstep %}
{% step %}
### Day 2 or 3: Confirm amounts
The customer checks their bank statement (or app), sees two deposits from "EVOLVE-VERIFY", and returns to your verification flow. They enter the two amounts. If they match, the verification succeeds.
{% endstep %}
{% endstepper %}
## Failure modes
The customer has up to 3 attempts to enter the right amounts. After the third wrong attempt, the verification fails and the customer is locked out for 24 hours.
Other failures:
<details>
<summary>Customer never returns</summary>
After 7 days without the customer entering the amounts, the verification expires. You can resend the flow with a fresh pair of deposits, but you'll be charged for the new attempt.
</details>
<details>
<summary>Deposits don't land</summary>
The deposits can fail (closed account, frozen account, mistyped routing/account number) — you'll get a `bank_verification.failed` webhook with the bank's reason code. Re-send the flow if it was a typo; otherwise the customer needs to use a different account.
</details>
<details>
<summary>Wrong amounts entered</summary>
If the customer enters the wrong amounts three times, the verification fails. This sometimes catches typos, sometimes catches fraud — someone trying to verify an account they don't own.
</details>
## When it's the right choice
Use micro-deposits when:
* You're verifying a US bank account at a smaller institution.
* Plaid was attempted and failed.
* Cost matters — at $0.80 vs $2.50 for Plaid, micro-deposits add up at volume.
* The customer is patient (B2B onboarding, marketplace seller signup) and the 1–2 day delay is acceptable.
Don't use micro-deposits as the primary method when:
* The customer needs to transact immediately (consumer checkout, real-time onboarding).
* You're paying out the customer in the next business day — you'd verify the account *after* paying out, defeating the point.
## Costs and limits
| | |
| --- | --- |
| **Cost per verification** | $0.80 |
| **Speed** | 1–2 business days |
| **Coverage** | Any US bank with valid ABA routing |
| **Re-verification** | Free for 90 days after initial |
| **Customer attempts** | 3 before lockout |
## What you get back
The same data as Plaid — masked account number, routing, account type, account holder name. Micro-deposits don't return balance or risk signals (no online banking session is opened), so you'll get less context.
## Related
* [Plaid instant](plaid-instant.md) — the faster option when supported.
* [Bank account verification](README.md) — parent flow.
* [Payments / Money movement](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/money-movement) — using a verified account for ACH.
references/example-site/products/identity/verification-flows/bank-account/plaid-instant.md
---
icon: bolt
description: One-tap, real-time bank account verification through Plaid.
---
# Plaid instant
Plaid instant is the fastest way to verify a customer's bank account. The customer signs into their bank from your verification flow; Evolve receives the verified account and routing numbers in real time. The whole experience takes 30–60 seconds.
It's the right default for any product where customer experience matters — checkout abandonment is real and 1–2 days of waiting for micro-deposits is enough to lose a sign-up.
## What the customer experiences
{% stepper %}
{% step %}
### "Connect your bank"
A button or link in your flow opens the Evolve hosted verification page. The customer picks their bank from a list of Plaid-supported institutions (or searches by name).
{% endstep %}
{% step %}
### Sign in to the bank
The customer signs in with their online banking credentials, on a Plaid-hosted page. Their credentials never touch Evolve or your servers — Plaid handles the bank session.
{% endstep %}
{% step %}
### Pick an account
Most customers have multiple accounts at the same bank. They pick the one they want to use — checking vs savings, primary vs secondary.
{% endstep %}
{% step %}
### Done
The flow returns to your site. The verified account appears on the customer's record in **Identity → Customers** with a verified badge. From there it can be used immediately for ACH debits, payouts, or whatever your product needs.
{% endstep %}
{% endstepper %}
## Coverage
Plaid covers ~12,000 US financial institutions, including all major retail banks and most credit unions. For a customer's specific bank, the [Plaid coverage checker](https://plaid.com/institutions) shows whether instant verification is supported.
For non-US accounts, Plaid's coverage is much narrower — currently only major banks in Canada, the UK, France, Spain, the Netherlands, and Ireland. Customers outside these countries fall back to micro-deposits or open banking flows specific to their region.
## What you get back
Once verified, you have on the customer record:
| Field | Example |
| --- | --- |
| Account number (masked) | `••••6789` |
| Routing number | `021000021` |
| Account type | `checking` / `savings` |
| Bank name | `Chase` |
| Account holder name (verified) | `Jordan Patel` |
| Available balance (optional) | `$4,182.50` |
| Currency | `USD` |
The masked account number is what you should display to the customer in your UI. The full account and routing numbers are accessible via the API for ACH debit operations, but never displayed in the dashboard or on receipts.
## Risk and fraud signals
In addition to the account data, Plaid returns a set of risk signals you can opt into:
* **Account age** — when the account was opened. Very new accounts (< 30 days) are higher fraud risk.
* **Recent NSF events** — non-sufficient-funds bounces in the last 90 days. Indicates the customer might not have funds for your debit.
* **Large recent deposits** — unusual incoming activity, sometimes a fraud signal.
* **Multiple Plaid links recently** — the same account being linked to many products in a short window is a classic synthetic-identity pattern.
These come back as a `risk_score` on the verification record, plus the individual signals. You decide how to act on them — block, manual review, or ignore.
## Costs and limits
| | |
| --- | --- |
| **Cost per verification** | $2.50 |
| **Speed** | 30–60 seconds |
| **Re-verification** | Free for 30 days after initial |
| **Coverage** | ~12,000 US banks + select international |
There's no monthly minimum or platform fee — you only pay per verification.
## When Plaid fails
Sometimes the customer can't get through Plaid — their bank is undergoing maintenance, their credentials don't work, they'd rather not share online banking access. The default Evolve flow handles this gracefully:
* The Plaid sheet shows a "Use another method" link at the bottom of the bank list.
* Clicking it switches the customer into the [micro-deposits](micro-deposits.md) flow seamlessly.
* The verification record links the two attempts so you can see both in the timeline.
If you'd rather not offer the fallback (e.g. you can't wait 1–2 days), turn it off in **Settings → Identity → Bank verification → Fallback**.
## Related
* [Micro-deposits](micro-deposits.md) — fallback when Plaid isn't available.
* [Bank account verification](README.md) — parent flow.
* [Payments / Money movement](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/money-movement) — using a verified account for ACH.
references/example-site/products/identity/verification-flows/bank-account/README.md
---
icon: building-columns
description: Confirm a bank account belongs to the customer before debiting or paying out.
---
# Bank account verification
Bank account verification proves the customer owns the account they've handed you — important before you debit them with ACH, before you push a payout to them, or before you accept them as a payment recipient on a marketplace.
Evolve supports two methods. **Plaid instant** is faster (one tap) but only works for banks Plaid covers. **Micro-deposits** work for any US bank account but take 1–2 business days.
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="warning" icon="lock" %}
**Bank account verification is a Growth and Enterprise feature.** Starter accounts can verify identities but not bank accounts.
{% endhint %}
{% endif %}
## Pick a method
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-bolt" style="color:$primary;">:bolt:</i></h3></td><td><strong>Plaid instant</strong></td><td>One-tap, real-time. Best customer experience.</td><td><a href="plaid-instant.md">plaid-instant.md</a></td></tr><tr><td><h3><i class="fa-coins" style="color:$primary;">:coins:</i></h3></td><td><strong>Micro-deposits</strong></td><td>Two small test deposits, customer confirms amounts. 1–2 business days.</td><td><a href="micro-deposits.md">micro-deposits.md</a></td></tr></tbody></table>
## When to use which
| Situation | Method |
| --- | --- |
| Customer's bank is on Plaid (most major US banks) | Plaid instant |
| Customer's bank isn't on Plaid (small credit unions, foreign banks) | Micro-deposits |
| You want every customer to have the same UX | Plaid first, fall back to micro-deposits |
| Speed is critical (real-time onboarding) | Plaid instant only |
| Cost is critical | Micro-deposits ($0.80 vs $2.50) |
The default flow Evolve generates tries Plaid first and falls back to micro-deposits automatically when Plaid can't help. You can override this in **Settings → Identity → Bank verification**.
## What gets verified
Both methods confirm:
* The **routing and account numbers** match a real, open account.
* The **account holder name** matches what the customer told you.
Plaid instant adds:
* The **balance** (helpful if you want to gate large debits on available funds).
* Recent **transaction history** (used by some teams for risk scoring).
Both methods generate a `bank_account.verified` webhook event when complete, with the masked account number and the verification method used.
## Storing the result
Once verified, the bank account is attached to a Customer record (see [Saved payment methods](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/accept-payments/saved-payment-methods) in Payments) and can be used for ACH debits or payouts without re-verification — until the customer's bank reissues credentials or you trigger a re-verification.
## What's logged
Every bank account verification creates an entry in the [audit log](../../compliance/audit-logs.md) with the method used, the verification status, and which member of your team initiated it (if it was triggered from the dashboard rather than the API).
## Related
* [Plaid instant](plaid-instant.md) — setup and customer experience.
* [Micro-deposits](micro-deposits.md) — what the customer sees, retry handling.
* [Payments / Money movement](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/concepts/money-movement) — how verified bank accounts are used in ACH flows.
references/example-site/products/identity/verification-flows/business/beneficial-ownership.md
---
icon: people-roof
description: Identify and verify the people who actually own a business — typically anyone with 25% or more.
---
# Beneficial ownership
When you verify a business, you're really verifying the people behind it — the **beneficial owners**. Regulators care about this because shell companies are how illicit money moves; if you don't know the humans behind the LLC, you don't really know the business.
Evolve's KYB flow handles ownership collection and verification together. The business operator declares the owners, Evolve verifies each one, and the result is a complete owner-by-owner verification record attached to the business.
## What counts as a beneficial owner
The definition Evolve uses (matching FinCEN's Customer Due Diligence rule and most equivalent EU/UK rules):
* Anyone who **owns 25% or more** of the business, directly or indirectly.
* Anyone who **exercises significant control** — typically a CEO, managing director, general partner, or trustee.
For most small businesses, that's 1–4 people. For businesses with complex ownership (parent companies, trusts, fund structures), it can require traversing several layers.
{% hint style="info" %}
The 25% threshold is a regulatory floor, not a ceiling. Some banks and Enterprise customers configure Evolve to verify owners down to 10% or even 5% for higher-risk verticals. Set this in **Settings → Identity → Business verification → Ownership threshold**.
{% endhint %}
## How owners are collected
The hosted KYB form walks the operator through declaring ownership:
{% stepper %}
{% step %}
### Add each owner
For each owner, the operator enters: legal name, date of birth, residential address, ownership percentage, and role (Owner, Officer, or both).
{% endstep %}
{% step %}
### Confirm 100% accounted
The form requires that declared ownership totals at least 75% (since anyone under 25% isn't a beneficial owner under the rule). If ownership is split among many small holders, the operator can declare "Ownership distributed below threshold" and Evolve only verifies the controllers.
{% endstep %}
{% step %}
### Each owner verifies separately
For each declared owner, Evolve generates an [identity verification](../identity-verification/README.md) link. The owner clicks it (typically in an email Evolve sends) and completes a document + selfie verification on their own device. They don't need to be in the same place as the operator.
{% endstep %}
{% endstepper %}
## Verifying complex ownership
For businesses owned in part by other entities — a holding company, a trust, a fund — Evolve walks down the chain:
* If an entity owns ≥25% of your customer business, Evolve adds that entity as a sub-KYB.
* The sub-KYB collects *its* beneficial owners.
* Each layer is verified independently.
This can take weeks for genuinely complex structures. The dashboard shows the full ownership tree with per-node status, so you can see what's blocking completion.
## What you get back
When all owners are verified, the business record shows:
* Each owner's name, ownership %, role, verification status, and screening result.
* The full ownership tree (as a graph in the dashboard).
* A consolidated **KYB status** of `verified`, `failed`, or `manual_review`.
For audit purposes, every artifact Evolve gathered (declaration form, verification IDs, sanctions screening results) is retained and downloadable as a single PDF report from the dashboard.
## Re-verifying when ownership changes
Beneficial ownership isn't static — businesses sell shares, partners leave, founders dilute. The dashboard's **Business → Ownership monitoring** tracks public records (state filings, SEC filings, Companies House) and alerts you when:
* A new majority owner is recorded.
* A previously verified owner's stake drops below the threshold.
* The business's registration or corporate status changes.
When an alert fires, the right move is usually to re-verify any new owners and confirm the existing record is still accurate. The hosted re-verification flow makes this a one-click operation for the business operator.
## Privacy
Beneficial owner PII is treated the same as any individual identity verification — encrypted at rest, retained per your [retention policy](../../compliance/data-retention.md), and accessible only to authorized members of your team.
Specifically, you can configure who on your team can see beneficial owners' PII vs. just the verification status. Most teams restrict PII access to compliance staff and use anonymized identifiers everywhere else.
## Related
* [Business verification (KYB)](README.md) — the parent flow.
* [Sanctions screening](sanctions-screening.md) — runs against each owner.
* [Identity verification](../identity-verification/README.md) — the per-owner check.
* [Compliance → Regional requirements](../../compliance/regional-requirements.md) — country-specific ownership rules.
references/example-site/products/identity/verification-flows/business/README.md
---
icon: briefcase
description: Verify a business and the people who own it — KYB for marketplaces, B2B platforms, and payouts.
---
# Business verification (KYB)
Business verification — KYB, "know your business" — confirms three things about a business you're onboarding: that the entity actually exists, that it's not on a sanctions or blocklist, and that the people you're dealing with are who they say they are (the **beneficial owners**).
You'll need it if you run a marketplace and onboard sellers, you operate a B2B platform that pays out to vendors, you sell to other businesses on credit, or any time you process payments on behalf of a third party.
{% if visitor.claims.unsigned.plan !== "enterprise" %}
{% hint style="warning" icon="lock" %}
**Business verification is an Enterprise feature.** It requires the data partnerships and compliance review processes only available on the Enterprise plan. [Talk to your account team](mailto:support@evolve.com) if you're considering it.
{% endhint %}
{% endif %}
## What's checked
A KYB verification is a bundle of separate checks, run together against the business and its owners:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-people-roof" style="color:$primary;">:people-roof:</i></h3></td><td><strong>Beneficial ownership</strong></td><td>Who owns ≥25% of the business — and verify each one.</td><td><a href="beneficial-ownership.md">beneficial-ownership.md</a></td></tr><tr><td><h3><i class="fa-ban" style="color:$primary;">:ban:</i></h3></td><td><strong>Sanctions screening</strong></td><td>Check the business and owners against sanctions lists.</td><td><a href="sanctions-screening.md">sanctions-screening.md</a></td></tr></tbody></table>
Plus, on every KYB:
* **Entity existence** — the business is registered with the state/country it claims, in good standing.
* **Tax ID match** — the EIN (or local equivalent) matches the registered name.
* **Address verification** — the business address resolves to a real location, not a virtual office.
## A typical KYB session
```mermaid
flowchart LR
A[Start] --> B[Collect business info]
B --> C[Verify entity]
C --> D[Collect beneficial owners]
D --> E[Verify each owner]
E --> F[Sanctions screen]
F --> G[Decision]
```
The whole flow takes 2–10 business days, depending on:
* The country of registration (US is fastest; some jurisdictions require manual record pulls).
* Whether all owners can be verified online or some require manual document review.
* Whether anything flags during sanctions screening.
For most US LLCs and corporations, KYB completes within 2 business days.
## Hosted vs. API-driven
Two integration shapes:
* **Hosted KYB form** — a single Evolve-hosted URL the business owner fills in. Best when the customer is human-in-the-loop. Most marketplaces use this.
* **API-driven** — your code submits the business info and triggers each check separately. Best when you've already collected everything in your own onboarding flow.
For the hosted flow, the URL is generated from **Identity → Business verifications → New** in the dashboard or via a single API call. For the API-driven path, see [Developers / Identity API → Business verifications](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/identity-api/business).
## Decisions
| Outcome | What it means |
| --- | --- |
| **Verified** | Entity, owners, and sanctions checks all clean. You can onboard. |
| **Failed** | One or more checks failed. The reason is on the timeline. |
| **Manual review** | Edge cases that Evolve's automated checks couldn't decide — typically owners with names that match (but probably aren't) sanctioned individuals. |
Manual review on KYB takes longer than on individual identity — typically 1–3 business days, since it often requires pulling additional records.
## Ongoing monitoring
KYB isn't a one-shot check. Sanctions lists update daily; ownership changes; businesses dissolve. Evolve runs **ongoing monitoring** against verified businesses — re-screening sanctions weekly and alerting you (via webhook and dashboard) when anything changes.
Ongoing monitoring is included with every KYB at no extra fee, for as long as the business is active in your account.
## Related
* [Beneficial ownership](beneficial-ownership.md) — collecting and verifying the owners.
* [Sanctions screening](sanctions-screening.md) — what lists Evolve checks against.
* [Audit logs](../../compliance/audit-logs.md) — every KYB action is logged.
* [Regional requirements](../../compliance/regional-requirements.md) — country-by-country obligations.
references/example-site/products/identity/verification-flows/business/sanctions-screening.md
---
icon: ban
description: Check the business and its owners against sanctions, embargo, and restricted-party lists.
---
# Sanctions screening
Sanctions screening checks both a business and its [beneficial owners](beneficial-ownership.md) against the lists of entities and individuals that governments restrict you from doing business with. It runs automatically as part of every [KYB verification](README.md), and continues running on a schedule for as long as the business is active in your account.
The lists matter. Onboarding a sanctioned business is a regulatory violation, not just a fraud problem — fines run to the millions per incident, and personal liability can attach to specific compliance officers.
## Lists Evolve checks against
| List | Issued by | Coverage |
| --- | --- | --- |
| **OFAC SDN** | US Treasury | Specially Designated Nationals — the most-cited US sanctions list |
| **OFAC sectoral** | US Treasury | Sector-specific sanctions (e.g. Russian financial sector) |
| **OFAC consolidated** | US Treasury | All other OFAC programs combined |
| **UN consolidated** | United Nations | UN Security Council sanctions |
| **EU consolidated** | European Council | EU restrictive measures |
| **UK HMT** | UK HM Treasury | UK financial sanctions |
| **DPL / EAR** | US Commerce | Denied Persons List, export controls |
| **Country-specific** | Various | ~40 national lists (Canada, Australia, Singapore, etc.) |
| **Adverse media** | Aggregated news | News mentions tied to financial crime risks |
The full list with version timestamps is in **Settings → Identity → Sanctions sources**. Evolve refreshes from each source daily.
## How matching works
A "match" isn't a literal string match — names on sanctions lists vary in spelling, transliteration, name order, and inclusion of middle names or patronyms. Evolve's matcher handles:
* **Romanization variants** — Mohammed / Mohammad / Muhammad / Mohamed.
* **Word order** — last-first vs first-last conventions across cultures.
* **Diacritic stripping** — Müller and Mueller treated as the same.
* **Diminutives** — Bill / William, Bob / Robert (English only).
* **Middle name handling** — John H. Smith and John Henry Smith match.
* **Date-of-birth confirmation** — narrows false positives by requiring DOB within a tolerance.
The result is a confidence score per potential match. Above the high-confidence threshold (default 0.92), the verification fails. Between 0.75 and 0.92, it goes to manual review. Below 0.75, it's treated as a non-match.
You can tune these thresholds per-tenant in **Settings → Identity → Sanctions sensitivity**. Lower thresholds catch more (but flood you with false positives); higher thresholds catch less (and miss true matches).
## When something matches
A match — even at high confidence — isn't always a true positive. The decision tree:
```mermaid
flowchart LR
A[Potential match] --> B{Confidence}
B -->|High| C[Failed: do not onboard]
B -->|Medium| D[Manual review]
B -->|Low| E[Treated as no match]
C --> F[Compliance team reviews]
D --> F
F --> G{Confirmed?}
G -->|Yes| H[Document and reject]
G -->|No| I[Override and proceed]
```
For high-confidence matches, the right default is to refuse the onboarding and document the reason. For medium-confidence matches that turn out to be false positives — there are a lot of John Smiths in the world — your compliance team can override the match with a documented reason and proceed.
Every override is permanently logged in the [audit log](../../compliance/audit-logs.md), with the operator's identity, the match details, and the stated reason. This is the trail your auditor will want.
## Ongoing monitoring
A clear screening result today doesn't stay clear forever. Evolve re-screens every active business **weekly** against fresh list snapshots. When a previously verified owner or business is added to a list:
* `screening.match_added` webhook fires immediately.
* The business is flagged in the dashboard with a red banner.
* Your compliance team receives an alert email.
You decide what to do — most teams suspend the business pending review and offboard if the match is confirmed.
The cost of ongoing monitoring is included with the original KYB at no extra fee, for as long as the business is active.
## What you can configure
Per your compliance program's needs:
* **List subset** — turn off lists that don't apply. Some teams skip adverse media because of the false-positive rate.
* **Confidence thresholds** — tighten or loosen.
* **Date-of-birth tolerance** — typically ±2 years; tighten for higher-risk businesses.
* **Geographic scope** — focus screening on businesses in countries you operate in.
## Adverse media specifics
Adverse media is the loosest screening category and produces the most false positives, because it's drawn from open-source news rather than maintained government lists. We recommend treating adverse media matches as **manual review only**, never as automatic rejection.
The categories of news Evolve flags:
* Money laundering and structuring
* Terrorism financing
* Human trafficking
* Sanctions evasion
* Cyber-crime and fraud
* Bribery and corruption
* Tax evasion
You can turn off specific categories in **Settings → Identity → Adverse media → Categories**.
## Related
* [Beneficial ownership](beneficial-ownership.md) — owners are screened individually.
* [Watchlist screening](../identity-verification/watchlist-screening.md) — the equivalent for individual identity verifications.
* [Audit logs](../../compliance/audit-logs.md) — every screening decision is logged.
* [Regional requirements](../../compliance/regional-requirements.md) — country-by-country sanctions obligations.
references/example-site/products/identity/verification-flows/identity-verification/document-review.md
---
icon: file-magnifying-glass
description: How Evolve confirms a government-issued ID is genuine, valid, and matches the customer.
---
# Document review
The document-review check is the first half of an identity verification. It looks at the photo of a government-issued ID — passport, driver's license, or national ID card — and decides whether it's real, current, and matches what the customer typed in.
## What's checked
Every document goes through five separate sub-checks:
| Sub-check | What it does |
| --- | --- |
| **Authenticity** | Compares the document against a database of templates for that country and type. Flags photoshopped documents, fake templates, and known forgery patterns. |
| **Expiry** | Reads the expiry date and rejects expired documents. |
| **Tampering** | Pixel-level analysis for image manipulation — re-glued laminates, replaced photos, edited text. |
| **MRZ / barcode parity** | For documents with machine-readable zones or PDF417 barcodes, confirms the printed data matches the encoded data. |
| **Data extraction** | Pulls the name, date of birth, document number, and expiry. Returns this on the verification result. |
The result is a per-sub-check pass/fail, plus an overall confidence score. The overall decision uses your strictness setting (see [Identity verification](README.md#configurable-strictness)).
## Supported documents
| Document type | Coverage |
| --- | --- |
| **Passport** | Every country except the OFAC-blocked list (~190 countries). |
| **Driver's license** | All 50 US states + DC, all Canadian provinces, all EU/EEA countries, UK, Australia, NZ, Japan. |
| **National ID card** | EU/EEA, UK, Singapore, Hong Kong, India (Aadhaar), and ~40 others. |
| **Residence permit** | EU/EEA, UK, US, Canada — for non-citizen residents. |
The full list is in **Settings → Identity → Supported documents**, with a search by country.
## What the customer sees
The hosted flow walks them through the capture:
{% stepper %}
{% step %}
### Pick country and type
A dropdown of countries (defaulted to the customer's IP-inferred country), then a list of document types valid for that country. If they don't have any of the listed documents, they can pick "Other" and the verification will land in manual review.
{% endstep %}
{% step %}
### Front capture
The camera opens with an outline overlay. Real-time feedback tells them when the document is in frame, in focus, and well-lit. Glare and motion blur are detected before submission.
{% endstep %}
{% step %}
### Back capture (if needed)
Driver's licenses and national ID cards typically have data on the back (barcode, magnetic stripe). Passports don't. Evolve only prompts for back capture when needed for the document type.
{% endstep %}
{% endstepper %}
The whole document phase typically takes 30–45 seconds.
## Failure reasons
When document review fails, the reason on the timeline tells you why:
<details>
<summary>document_expired</summary>
The document's expiry date is in the past. The customer needs to provide a current document — they can retry with one if they have one.
</details>
<details>
<summary>document_tampered</summary>
Pixel analysis detected manipulation — usually a replaced photo or edited text. This is treated as a fraud signal; the customer is locked out of retries until you (or Evolve's manual reviewers) approve another attempt.
</details>
<details>
<summary>document_unrecognized</summary>
The document doesn't match any template in our database. Most often this is a partial capture (the camera missed an edge), but it can also be an unsupported document type the customer didn't realize was unsupported. Retries usually succeed once the customer recaptures.
</details>
<details>
<summary>data_mismatch</summary>
The data extracted from the document doesn't match what the customer typed in (typically a different name spelling). The customer can correct their input and retry.
</details>
## Reviewing failed verifications
Failed verifications appear in **Identity → Sessions → Failed**. Each one shows the document image, the per-sub-check result, and the extracted data. If you believe a verification was incorrectly failed, you can submit it for human re-review with one click — it goes to Evolve's reviewer team and typically resolves within an hour.
## Privacy and storage
Documents are encrypted at rest with per-tenant keys and retained per your [retention policy](../../compliance/data-retention.md) (default: 30 days). They're never used for training Evolve's models without explicit, per-account opt-in.
## Related
* [Selfie and liveness](selfie-and-liveness.md) — the second half of identity verification.
* [Identity verification](README.md) — the parent flow.
* [Audit logs](../../compliance/audit-logs.md) — every document review is logged.
references/example-site/products/identity/verification-flows/identity-verification/README.md
---
icon: id-card
description: Verify an individual customer with a government-issued document and a live selfie.
---
# Identity verification
Identity verification is Evolve's flow for confirming that a person is who they say they are. It runs two checks together — a **document review** of a government-issued ID, and a **selfie liveness check** that matches the photo on the document to a live capture of the customer's face.
For most consumer-facing products this is the only verification you need. For higher-risk products and Enterprise customers, an optional **watchlist screening** check can be layered on top.
## What the customer experiences
The hosted flow takes 60–90 seconds end to end, on average:
{% stepper %}
{% step %}
### Pick a country and document
The customer picks the country that issued their ID and the document type. Evolve supports passports, driver's licenses, and national ID cards in <code class="expression">space.vars.document_supported_countries</code> countries.
{% endstep %}
{% step %}
### Capture the document
The flow opens the camera and walks the customer through capturing the front (and back, where required). Glare and blur detection give live feedback so the captures are usable.
{% endstep %}
{% step %}
### Capture a selfie
A 3-second passive liveness capture. The customer holds the camera in front of them; no head turns or specific gestures required.
{% endstep %}
{% step %}
### Decision
The result lands in your dashboard within seconds — Verified, Failed, or (in a small fraction of cases) Manual review.
{% endstep %}
{% endstepper %}
## The checks
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-file-magnifying-glass" style="color:$primary;">:file-magnifying-glass:</i></h3></td><td><strong>Document review</strong></td><td>Authenticity, expiry, tampering, and data extraction.</td><td><a href="document-review.md">document-review.md</a></td></tr><tr><td><h3><i class="fa-face-smile" style="color:$primary;">:face-smile:</i></h3></td><td><strong>Selfie and liveness</strong></td><td>Confirms the person matches the document.</td><td><a href="selfie-and-liveness.md">selfie-and-liveness.md</a></td></tr></tbody></table>
## Decisions and reasons
Every verification ends in one of three outcomes. The reason is on the timeline:
| Outcome | What it means | Common reasons |
| --- | --- | --- |
| **Verified** | All checks passed; you can trust the identity. | — |
| **Failed** | One or more checks failed; the identity is not trusted. | `document_expired`, `document_tampered`, `selfie_mismatch`, `liveness_failed` |
| **Manual review** | An edge case Evolve isn't confident about. A human reviewer (yours or ours) gets a second look. | `low_confidence_match`, `document_partially_obscured` |
Manual review typically resolves within an hour during business hours, longer overnight. You can configure who reviews — your team, Evolve's review team, or both — in **Settings → Identity → Manual review**.
## Configurable strictness
You can tune how strict each check is in **Settings → Identity → Strictness**. Three presets cover most cases:
* **Standard** (default) — balanced for consumer onboarding. Used by most teams.
* **Strict** — tighter thresholds, more manual reviews. Right for higher-risk products like crypto, gambling, or pharmacy.
* **Permissive** — looser thresholds, fewer manual reviews. Right for low-stakes flows like creator platforms or community products.
You can also build custom rules — e.g. "Strict for transactions over $1,000, Standard otherwise" — in the same settings panel.
## Re-running for the same customer
If a customer's first attempt fails, you can let them retry. By default, each customer gets up to 3 attempts within a 24-hour window before being locked out. The retry policy is in **Settings → Identity → Retry policy**.
A separate **re-verification** API lets you re-run the full flow against an existing customer at any later point — for example, after a chargeback or before a high-value transaction.
## Related
* [Document review](document-review.md) — how Evolve authenticates the document.
* [Selfie and liveness](selfie-and-liveness.md) — the biometric match.
* [Watchlist screening](watchlist-screening.md) — optional Enterprise screening layer.
* [Compliance → Audit logs](../../compliance/audit-logs.md) — every decision is logged.
references/example-site/products/identity/verification-flows/identity-verification/selfie-and-liveness.md
---
icon: face-smile
description: A short, passive selfie that confirms the live person matches the document photo.
---
# Selfie and liveness
The selfie-and-liveness check is the second half of an identity verification. It confirms two things: that the person in front of the camera is the same person on the document, and that the camera is seeing a live human (not a photo, video, mask, or deepfake).
Evolve's current implementation is **passive liveness 2.0** — no head turns, no smile-on-command, no spelling out numbers. The customer just holds the camera in front of their face for about three seconds. The model decides liveness from subtle signals (skin texture, micro-movements, lighting consistency) without asking the customer to do anything.
## What gets compared
The check produces two scores:
| Score | What it means | Threshold (Standard preset) |
| --- | --- | --- |
| **Match score** | Similarity between the selfie and the photo on the document. 0.0 (no match) to 1.0 (identical). | ≥ 0.85 to pass |
| **Liveness score** | Confidence the selfie is from a live human. 0.0 to 1.0. | ≥ 0.95 to pass |
Both scores are visible on the verification timeline. You can see at a glance whether a manual review is borderline (e.g. match score 0.83) or clearly fraudulent (match score 0.41).
## What the customer sees
```mermaid
flowchart LR
A[Camera opens] --> B[Position face<br>in oval]
B --> C[Hold for 3s]
C --> D[Capture]
D --> E[Result]
```
The whole step takes under 10 seconds end to end. The model needs about 3 seconds of stable video to make a confident liveness call.
If the customer's environment is bad (very dim, very bright, blurry camera), the flow detects it and prompts a retry before submission. This avoids "fail and ask the customer to start over" loops.
## Failure reasons
<details>
<summary>selfie_mismatch</summary>
The face on the selfie doesn't match the photo on the document. This can be a real fraud signal, or a benign issue — different hairstyle, face mask in the document photo, large age gap between document issuance and now. Borderline cases (match score between 0.65 and 0.85) go to manual review.
</details>
<details>
<summary>liveness_failed</summary>
The model is confident it's not seeing a live human. Usually a photo of a photo (replay attack), a video held in front of the camera, or a high-quality mask. Treated as a fraud signal — the customer is locked out of retries until manually approved.
</details>
<details>
<summary>capture_quality</summary>
The selfie was too dark, too blurry, or too occluded to score reliably. Customer can retry; this isn't a fraud signal.
</details>
<details>
<summary>customer_abandoned</summary>
The customer started the selfie step but didn't complete it within 5 minutes. The verification is left in `pending` until they return, or until 24 hours later when it auto-expires.
</details>
## Spoof attempt detection
Evolve's liveness model is hardened against the common attack patterns:
* **Photo of a photo** (printed picture held to camera) — detected via texture and reflection analysis.
* **Video replay** (recorded selfie played from another phone) — detected via screen moiré patterns and frame-to-frame consistency.
* **Mask attacks** (silicone or printed masks) — detected via micro-movement and depth cues.
* **Deepfakes** (real-time face-swap models) — detected via specific artifacts that differ from real cameras.
Detected spoofs land on the verification timeline as `spoof_detected` with the attack type. They're also surfaced in the [audit log](../../compliance/audit-logs.md) and can be alerted on via webhook.
## When liveness isn't required
For some product flows, you may want the document check without the selfie — for example, age verification on adult-only content where the document is sufficient. In **Settings → Identity → Required checks**, you can untoggle the selfie requirement per flow type.
## Privacy
Selfies are processed in memory for liveness scoring and discarded — only the score is retained. The match comparison against the document photo also produces only a score, not a stored embedding. Per your [retention policy](../../compliance/data-retention.md), the selfie image itself can be retained for review or auto-purged immediately.
## Accessibility
The selfie flow has been designed for accessibility:
* No timed gestures or tasks.
* Voice and screen-reader prompts walk the customer through positioning.
* High-contrast mode and large-text mode available.
* If a customer can't complete a selfie at all (camera-blind, severe motor impairment, no functional camera), the flow offers a manual-review path with an alternative ID document upload.
## Related
* [Document review](document-review.md) — the first half of identity verification.
* [Identity verification](README.md) — the parent flow.
* [Watchlist screening](watchlist-screening.md) — optional Enterprise screening layer.
references/example-site/products/identity/verification-flows/identity-verification/watchlist-screening.md
---
icon: shield-halved
hidden: true
description: Screen verified identities against sanctions, PEP, and adverse media lists. Enterprise only.
---
# Watchlist screening
{% hint style="warning" %}
**This page is intentionally hidden from the main navigation.** Watchlist screening is an advanced compliance feature that most teams don't need. If you're not on Enterprise, or you're not in a regulated vertical, you can safely ignore this page.
{% endhint %}
Watchlist screening checks an identity against four kinds of list:
* **Government sanctions lists** — OFAC SDN, UN, EU, UK HMT, and ~40 country-specific lists.
* **Politically exposed persons (PEPs)** — current and former senior public officials, their family, and close associates.
* **Adverse media** — recent news mentions tying the person to financial crime, terrorism, or other reputational risks.
* **Internal blocklists** — names you've added yourself, e.g. customers you've terminated for fraud.
It's run on top of an identity verification — the document and selfie checks confirm the person is who they say, and the watchlist check decides whether you're allowed to do business with them.
{% if visitor.claims.unsigned.plan !== "enterprise" %}
{% hint style="warning" icon="lock" %}
**Watchlist screening is Enterprise-only.** It requires the data partnerships and ongoing-monitoring infrastructure available only on the Enterprise plan. [Talk to your account team](mailto:support@evolve.com).
{% endhint %}
{% endif %}
## When you need it
You probably need watchlist screening if:
* You operate in a regulated vertical (financial services, money transmission, crypto, gambling, pharma).
* You're a marketplace and your sellers are subject to sanctions screening as a payment-aggregator obligation.
* You've been told by your bank, your auditor, or your compliance team that you need it.
You probably don't need it for typical e-commerce, SaaS, or community products.
## How matching works
Watchlist matching is a fuzzy-name match, not an exact-string lookup. Names on lists vary in spelling, transliteration, and order — Mohammed vs. Mohammad, Vladimir Vladimirovich Putin vs. Putin, Vladimir. Evolve's matcher accounts for all of this.
For each identity verified, the matcher returns:
| Result | What it means |
| --- | --- |
| **Clear** | No matches above the configured threshold. |
| **Match** | One or more names matched at high confidence. Verification status becomes `failed` and the customer cannot proceed without manual override. |
| **Possible match** | Match at moderate confidence. Verification goes to manual review. |
The threshold is configurable in **Settings → Identity → Screening sensitivity**. Most teams keep the default — it's calibrated to balance false-positive rate against the consequences of missing a true match.
## Ongoing monitoring
A clear screening result today doesn't stay clear forever — sanctions lists update daily, and a previously unsanctioned person can be added at any time. Evolve runs **ongoing monitoring** automatically:
* Every verified identity is **re-screened weekly** against the latest list snapshots.
* If a previously clear identity matches a newly added entry, you get a `screening.match_added` webhook and a dashboard alert.
* You decide what to do — most teams suspend the customer pending a manual review.
The cost is $0.10 per re-screen, billed against your monthly verification volume. You can opt out of ongoing monitoring per-verification via the strictness settings if a one-time check is sufficient.
## Adverse media
Adverse media screening is the loosest of the four categories — it returns any news mentions tying the person to specific risk topics:
* Money laundering and financial crime
* Terrorism financing
* Corruption and bribery
* Trafficking
* Cyber-crime and fraud
Because adverse media draws from open-source news, it produces more false positives than sanctions or PEP lists. Most teams treat adverse media matches as a manual-review trigger rather than an automatic block.
## What's logged
Every screening result is a permanent entry in your [audit log](../../compliance/audit-logs.md), including:
* The lists checked, with version timestamps.
* The matches found (or absence thereof).
* Any manual override applied, by whom, with the stated reason.
This is the audit trail your auditor and your bank will want to see.
## Related
* [Identity verification](README.md) — screening sits on top of identity verification.
* [Business verification → Sanctions screening](../business/sanctions-screening.md) — the equivalent check for businesses.
* [Audit logs](../../compliance/audit-logs.md) — the screening audit trail.
* [Regional requirements](../../compliance/regional-requirements.md) — when screening is mandatory.
references/example-site/products/identity/verification-flows/README.md
---
icon: list-check
description: The three verification flows in Evolve — when to use which, and how they fit together.
---
# Verification flows
Evolve verifies three different things — individuals, bank accounts, and businesses. They're separate flows with separate UIs and (often) separate fees, but they share one dashboard, one customer record, and one decision API.
Most teams start with **identity verification**. Add **bank account verification** when you take ACH or want to send payouts to consumers. Add **business verification** if you onboard businesses (marketplaces, B2B platforms, payouts to vendors).
## Pick a flow
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-id-card" style="color:$primary;">:id-card:</i></h3></td><td><strong>Identity verification</strong></td><td>Document + selfie for individual customers.</td><td><a href="identity-verification/README.md">README.md</a></td></tr><tr><td><h3><i class="fa-building-columns" style="color:$primary;">:building-columns:</i></h3></td><td><strong>Bank account verification</strong></td><td>Confirm a bank account belongs to the customer.</td><td><a href="bank-account/README.md">README.md</a></td></tr><tr><td><h3><i class="fa-briefcase" style="color:$primary;">:briefcase:</i></h3></td><td><strong>Business verification (KYB)</strong></td><td>Verify a business and its beneficial owners.</td><td><a href="business/README.md">README.md</a></td></tr></tbody></table>
## A decision tree
```mermaid
flowchart LR
Start[Onboarding<br>a customer] --> Q1{Individual<br>or business?}
Q1 -->|Individual| ID[Identity verification]
Q1 -->|Business| KYB[Business verification]
ID --> Q2{Accept ACH<br>or send payouts?}
KYB --> Q2
Q2 -->|Yes| Bank[Bank verification]
Q2 -->|No| Done[Done]
Bank --> Done
```
## How the flows compose
The three flows aren't mutually exclusive. A typical marketplace onboarding might run all three:
1. **Business verification** on the seller entity (KYB).
2. **Identity verification** on each beneficial owner.
3. **Bank account verification** for the seller's payout account.
Evolve groups these into a **verification bundle** so they appear as a single onboarding case in the dashboard with one combined status. Bundles are configured in **Settings → Identity → Verification bundles**.
## What's gated by plan
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="info" icon="layer-group" %}
**You're on Starter** — identity verification only. To add bank or business verification, [upgrade your plan](https://gitbook.com).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="layer-group" %}
**You're on Growth** — identity and bank verification are enabled. Business verification is an Enterprise feature.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="layer-group" %}
**You're on Enterprise** — all flows enabled, including watchlist and PEP screening on identity verification.
{% endhint %}
{% endif %}
| Flow | Starter | Growth | Enterprise |
| --- | :---: | :---: | :---: |
| Identity (document + selfie) | ✅ | ✅ | ✅ |
| Bank account (Plaid + micro-deposits) | — | ✅ | ✅ |
| Business verification (KYB) | — | — | ✅ |
| Watchlist + PEP screening | — | — | ✅ |
| Volume cap | 100/mo | 1,000/mo | Custom |
## How verifications appear in the dashboard
Every verification — regardless of flow — lands in **Identity → All sessions**. The session row shows the flow, the subject (customer or business), the status, and the time. Click in to see the full timeline of checks, the documents collected, and the final decision with reasoning.
The **All sessions** view filters by status, flow, and customer cohort, and exports to CSV the same way [Payments reports](https://app.gitbook.com/s/w3LlITSOQye8o4wjsQXV/reporting/standard-reports) do.
## Re-verification
Verifications aren't permanent. Some teams re-verify on a schedule (every 12 months for high-risk customers), some only on a specific signal (chargeback, account takeover suspicion, large transaction). Evolve's re-verification API lets you trigger a fresh flow against an existing customer; the result is added to their session history without overwriting the prior decision.
For the policy patterns most teams use, see [Compliance → Regional requirements](../compliance/regional-requirements.md#re-verification-cadence).
references/example-site/products/payments/.gitbook/includes/environments.md
---
title: Test and live environments
---
Evolve has two fully separate environments. They share no data — keys, customers, charges, and webhooks all exist independently in each.
| Environment | API base URL | Dashboard | Key prefix |
| --- | --- | --- | --- |
| Test | <code class="expression">space.vars.api_test</code> | <code class="expression">space.vars.dashboard_test</code> | `sk_test_` / `pk_test_` |
| Live | <code class="expression">space.vars.api_live</code> | <code class="expression">space.vars.dashboard_live</code> | `sk_live_` / `pk_live_` |
Test mode accepts only test card numbers (see [Create a charge](../../accept-payments/create-a-charge.md#test-cards)). No money moves, and no webhooks fire to anything other than test endpoints.
references/example-site/products/payments/.gitbook/vars.yaml
api_live: https://api.evolve.com
api_test: https://api.test.evolve.com
dashboard_live: https://dashboard.evolve.com
dashboard_test: https://dashboard.test.evolve.com
support_email: support@evolve.com
status_page: https://status.evolve.com
idempotency_ttl_hours: 24
settlement_time_utc: 06:00 UTC
webhook_retry_window_days: 3
references/example-site/products/payments/accept-payments/3d-secure.md
---
icon: shield-halved
description: When 3-D Secure is required, when it's optional but worth it, and how Evolve handles it.
---
# 3-D Secure and SCA
3-D Secure (3DS) is an extra step at checkout where the cardholder authenticates with their bank — usually a one-time code, a push notification, or a biometric prompt. When 3DS succeeds, the card-issuing bank takes on the liability for fraud chargebacks. When it's not used, you do.
3DS is **required** for cards issued in the EU and UK (under SCA rules) and **optional but often worth it** elsewhere.
## When Evolve applies 3DS automatically
You don't have to configure anything for required cases. Evolve detects them based on the card BIN and the transaction context:
| Trigger | Result |
| --- | --- |
| Card issued in the EEA, UK, or India | 3DS challenge required |
| Transaction over €30 with a European card | 3DS challenge required (with limited exemptions) |
| Card flagged as high-risk by the issuer | 3DS challenge required |
| Recurring payment using a saved card with a recorded mandate | Often exempt |
| Low-value transaction (<€30) | Often exempt under low-value-payment rules |
## When you might want to require 3DS
Even where it's not required, some teams turn 3DS on for high-value payments or known-risk patterns. The trade-off is real:
{% columns %}
{% column width="50%" %}
#### Pros
* Liability shifts to the issuer for fraud chargebacks.
* Approval rates often go up on cards the issuer was about to decline.
* Strong signal to the network that you take fraud seriously.
{% endcolumn %}
{% column width="50%" %}
#### Cons
* Adds 5–15 seconds to the checkout flow.
* 1–3% of customers abandon during the challenge.
* Costs $0.05 per attempt outside required cases.
{% endcolumn %}
{% endcolumns %}
## Configuring 3DS rules
In **Settings → Risk → 3-D Secure**, you can set rules for when 3DS should be requested:
* **Always** — every payment goes through 3DS.
* **Required only** (default) — Evolve handles required cases; everything else skips 3DS.
* **By rule** — your own rules layered on top of the required cases.
A rule is a condition (`amount`, `currency`, `country`, `payment_method`, `customer_history`) and an action (`require_3ds`, `skip_3ds`). Most teams start with one rule:
> Require 3DS when amount is over $500 and the customer has no prior successful payments.
## What customers see
Three flows, depending on the card and the issuer:
<details>
<summary>Frictionless</summary>
The issuer authenticates the customer in the background based on signals Evolve sends — device, browser, transaction history. The customer sees nothing extra. About 60% of European 3DS challenges resolve frictionlessly today.
</details>
<details>
<summary>Challenge</summary>
The customer is asked to authenticate — usually a one-time code, push notification, or biometric in their banking app. Takes 5–15 seconds. After they confirm, the payment continues automatically.
</details>
<details>
<summary>Failure</summary>
The customer fails to authenticate (wrong code, timeout, declined the push). The payment fails with `authentication_required` or `authentication_failed`. They can try again with a different card.
</details>
## SCA exemptions
Strong Customer Authentication (SCA) is the EU regulation behind 3DS. It allows a few exemptions where authentication can be skipped without losing the liability shift entirely:
| Exemption | Used when |
| --- | --- |
| **Low-value** | Transaction under €30 (max 5 in a row, then forced auth) |
| **Trusted beneficiary** | Customer has added you to their bank's trusted-merchants list |
| **Merchant-initiated transaction** | Recurring charge against a previously authenticated card with a recorded mandate |
| **Transaction risk analysis (TRA)** | Evolve's risk score is low enough that the issuer accepts a frictionless flow |
Evolve applies exemptions automatically when they fit. You can see which exemption was used (if any) on the payment timeline.
## Related
* [Saved payment methods](saved-payment-methods.md) — mandates and merchant-initiated transactions.
* [Smart routing](smart-routing.md) — pairing 3DS with the most likely-to-approve route.
* [Disputes and chargebacks](../reconciliation/disputes.md) — what the liability shift means in practice.
references/example-site/products/payments/accept-payments/failover.md
---
icon: arrows-spin
description: Stay up when an acquirer or network goes down.
---
# Failover and retries
Card-acquiring infrastructure is reliable, but it isn't perfect. Once or twice a year, a major acquirer or network has a partial outage — sometimes for an hour, occasionally for most of a day. Failover gives Evolve permission to route around an outage automatically, so your customers keep paying through it.
{% if visitor.claims.unsigned.plan !== "enterprise" %}
{% hint style="warning" icon="lock" %}
**Failover is an Enterprise feature.** It requires multiple active acquirer agreements, which most non-Enterprise customers don't maintain. If you're considering it, [talk to your account team](mailto:support@evolve.com).
{% endhint %}
{% endif %}
## How it works
Failover sits on top of [smart routing](smart-routing.md). Where smart routing optimizes for the best route per payment, failover reacts when a route — or a whole network — starts misbehaving.
```mermaid
flowchart LR
A[Payment created] --> B{Primary acquirer healthy?}
B -->|Yes| C[Route to primary]
B -->|No, degraded| D[Route to secondary]
C --> E{Auth response in 8s?}
E -->|Yes| F[Return result]
E -->|No, timeout| G[Retry on secondary]
D --> F
G --> F
```
Two signals trigger failover:
* **Acquirer health** — Evolve continuously monitors each acquirer's success rate, latency, and error mix. When a metric crosses a threshold for more than 60 seconds, that acquirer is temporarily marked degraded and traffic shifts to a backup.
* **Per-payment timeout** — if a single authorization request hasn't responded in 8 seconds, Evolve retries against the secondary acquirer in parallel and returns whichever responds first.
## What you configure
Failover is configured per acquirer pair in **Settings → Routing → Failover**. For each region, you set:
* **Primary acquirer** — the default route.
* **Secondary acquirer** — used during outages or timeouts.
* **Tertiary acquirer** *(optional)* — used if both primary and secondary are degraded.
* **Auto-recovery threshold** — how good the primary's success rate has to be before traffic comes back.
Most Enterprise customers have one or two pairs configured (US-domestic and EU-domestic) and let Evolve manage the rest from defaults.
## Limits
Failover only works between acquirers you have agreements with. If you're single-acquirer in a region, there's nothing to fail over to — you'll see the outage as your customers do. Adding a second acquirer takes a few weeks of paperwork; your account team can start the process if you don't have one yet.
It also doesn't apply to:
* **ACH, SEPA, BACS** — bank rails are single-path by design.
* **3-D Secure challenges** — the issuer's authentication server is the only path; we can't fail over.
* **Disputes and refunds** — these route to the original acquirer of the underlying payment.
## During an outage
When failover activates, two things happen:
1. **A banner appears in the dashboard** listing the affected acquirer and the time it was first marked degraded.
2. **Webhook events `routing.acquirer_degraded` and `routing.acquirer_recovered`** fire if you've subscribed to them — useful if you want to surface the status in your own ops tooling.
You don't need to do anything during an outage — the system manages itself. After it's over, the **Routing report** shows the failover events alongside the rest of the day's payments.
## Testing it
You can simulate a failover from **Settings → Routing → Test failover** in test mode. The simulator marks a fake acquirer degraded and runs a test payment through the secondary, so you can see what the timeline looks like without waiting for a real incident.
## Related
* [Smart routing](smart-routing.md) — picking the best route in normal conditions.
* [Routing report](../reporting/standard-reports.md#routing-report) — seeing failover activity over time.
* [Status page](https://gitbook.com) — Evolve's own platform availability.
references/example-site/products/payments/accept-payments/README.md
---
description: Charges, saved methods, 3-D Secure, smart routing, and failover.
icon: bolt
---
# Accept payments
This is the practical core of Evolve Payments — how to take money from a customer, what happens behind the scenes, and which controls you can turn on as your needs grow.
## Start here
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-circle-dollar-to-slot" style="color:$primary;">:circle-dollar-to-slot:</i></h3></td><td><strong>Take a payment</strong></td><td>The four channels for accepting money.</td><td><a href="take-a-payment.md">take-a-payment.md</a></td></tr><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><strong>Saved payment methods</strong></td><td>Charge the same customer again later.</td><td><a href="saved-payment-methods.md">saved-payment-methods.md</a></td></tr><tr><td><h3><i class="fa-shield-halved" style="color:$primary;">:shield-halved:</i></h3></td><td><strong>3-D Secure and SCA</strong></td><td>Liability shift and the EU rules.</td><td><a href="3d-secure.md">3d-secure.md</a></td></tr></tbody></table>
## Optimize
Once your basic flow is in production, these controls help you push the approval rate up.
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-route" style="color:$primary;">:route:</i></h3></td><td><strong>Smart routing</strong></td><td>Pick the route most likely to approve. <em>Growth+</em></td><td><a href="smart-routing.md">smart-routing.md</a></td></tr><tr><td><h3><i class="fa-arrows-spin" style="color:$primary;">:arrows-spin:</i></h3></td><td><strong>Failover and retries</strong></td><td>Re-route around acquirer outages. <em>Enterprise</em></td><td><a href="failover.md">failover.md</a></td></tr></tbody></table>
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="info" icon="arrow-up-right-from-square" %}
**Smart routing and failover are Growth and Enterprise features.** They're listed here so you can plan for them — see [pricing](https://gitbook.com) when you're ready to upgrade.
{% endhint %}
{% endif %}
## How a payment moves
Whether the payment is taken via a hosted link, embedded Checkout, or a custom integration, the same five things happen behind the scenes:
{% stepper %}
{% step %}
### Customer confirms
The customer enters card details on the hosted checkout, embedded Element, or your own form, then taps **Pay**.
{% endstep %}
{% step %}
### Evolve picks a route
If [smart routing](smart-routing.md) is on, Evolve picks the acquirer most likely to approve this card on this network. Otherwise the default acquirer is used.
{% endstep %}
{% step %}
### Network and issuer respond
The chosen acquirer sends the authorization to the card network. The issuer approves or declines, usually within a second.
{% endstep %}
{% step %}
### Capture happens
For one-step payments (the default), capture happens immediately. For two-step, you capture later from the dashboard or your code. See [Payment lifecycle](../concepts/payment-lifecycle.md).
{% endstep %}
{% step %}
### Funds land on your balance
The payment shows up as **Captured** on your dashboard within seconds, and is counted toward the next [settlement](../reconciliation/settlement-files.md). Anyone subscribed to webhooks gets a `payment.succeeded` event at this point.
{% endstep %}
{% endstepper %}
## Related
* [Payment lifecycle](../concepts/payment-lifecycle.md) — every state a charge can be in.
* [Errors and retries](../concepts/errors-and-retries.md) — what to do when a charge fails.
* [Webhooks](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks) — events emitted by every charge.
references/example-site/products/payments/accept-payments/saved-payment-methods.md
---
icon: bookmark
description: Charge the same customer again later — for subscriptions, repeat orders, or one-tap checkouts.
---
# Saved payment methods
Once a customer has paid you once, you can save their card or bank account and charge it again without asking for the details — for renewals, repeat purchases, or saving them an entry on their next visit.
## When to save a method
A saved method makes sense when:
* You bill on a recurring schedule (subscriptions, memberships).
* You expect repeat purchases (marketplaces, food delivery, B2B re-orders).
* You want to offer one-tap checkout on a future visit.
It does **not** make sense — and may not be allowed — to save a card "just in case." Card-network rules require the customer to explicitly agree to a future charge at the time of the first payment, and to know the rough cadence (monthly, on-demand, per-trip).
## How it works
Saved methods are attached to a **Customer** record. When you create a payment for that customer with a saved method, Evolve uses the stored token — neither you nor the customer ever handles the card number.
```mermaid
flowchart LR
A[First payment] -->|"customer agrees"| B[Method saved to Customer]
B --> C[Future payment 1]
B --> D[Future payment 2]
B --> E[...]
```
## Setting it up
{% stepper %}
{% step %}
### Capture consent
On your first checkout, show the customer a checkbox or clear text — "Save this card for future purchases" — that they must opt into. The wording is yours; the consent is required.
{% endstep %}
{% step %}
### Save the method
In hosted Checkout and Elements, this is a `setup_future_usage` toggle on the session. Once the payment succeeds, the method is attached to the customer.
In **Payments → Customers**, you'll see the saved method on the customer's profile — including the brand, last 4, expiry, and which currency it can be used for.
{% endstep %}
{% step %}
### Charge it later
From the dashboard, open the customer and click **New payment** — the saved method is preselected. From your code, reference the method by id when creating the next payment.
{% endstep %}
{% endstepper %}
## Mandates and recurring rules
For ACH, SEPA, and BACS, "saving" the method also means recording a **mandate** — the customer's authorization to debit them on a stated cadence. Evolve handles mandate text and signature capture on its hosted UIs. If you build your own UI, you must collect the mandate yourself; see the [Direct debit guide](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/direct-debit-mandates).
Cards don't require a formal mandate, but the network distinguishes between four types of subsequent charge:
| Type | Example |
| --- | --- |
| **Customer-initiated** | The customer clicks "Re-order" on your site. |
| **Merchant-initiated, scheduled** | A monthly subscription renewal. |
| **Merchant-initiated, unscheduled** | An e-commerce store charging a stored card after restock. |
| **Customer-not-present, account top-up** | A wallet that auto-tops up when the balance falls below a threshold. |
You set the type when you create the payment. The right value matters for approval rates and dispute outcomes — see the [recurring payments guide](https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/recurring-payments) for a deeper walkthrough.
## Updating a card before it expires
Cards expire. To reduce involuntary churn, Evolve participates in **account updater** services from Visa, Mastercard, and Amex. When a customer's bank reissues their card, the updated details are pulled in automatically — usually a week or two before the old expiry. You'll see the new expiry on the customer's saved method without any action on your part.
Account updater is on by default on Growth and Enterprise. On Starter you can enable it from **Settings → Cards → Account updater**.
## Removing a saved method
Customers can remove their own saved methods from the receipt email or from your customer portal (if you've built one). You can also remove a method from the dashboard — useful when a customer asks support to forget them.
Removing a method does not affect past payments — it only stops future charges from succeeding against that token.
references/example-site/products/payments/accept-payments/smart-routing.md
---
icon: route
description: Pick the route most likely to approve each payment — automatically.
---
# Smart routing
Most card payments can take more than one path to the issuer. Smart routing picks the path most likely to approve, based on the card's BIN, the transaction context, and historical success rates Evolve has observed across millions of payments.
For most teams, smart routing recovers 1–3% of payments that would otherwise be declined — without any code changes on your end.
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="warning" icon="lock" %}
**Smart routing is a Growth and Enterprise feature.** Starter accounts use a single default acquirer per region. To enable smart routing, [upgrade your plan](https://gitbook.com).
{% endhint %}
{% endif %}
## What it does
For each card payment, Evolve evaluates:
* **Card BIN** — which issuer, which country, which network.
* **Transaction shape** — amount, currency, merchant category, customer history.
* **Acquirer history** — recent approval rates for similar cards on each available acquirer.
It then routes the payment through the acquirer with the best expected approval rate. If that acquirer declines with a soft decline (issuer unavailable, processing error), it can also retry through a backup acquirer — turning a near-miss into an approval.
{% hint style="info" %}
Smart routing only changes which acquirer processes the payment. It never changes the customer-facing flow, the receipt, or the funds path back to you.
{% endhint %}
## Turning it on
In **Settings → Routing**, toggle **Smart routing** on. The defaults are sensible — most teams don't customize further. Evolve picks acquirers from your existing acquirer agreements; it never adds a new processor without your sign-off.
You can override the defaults with rules:
| Condition | Action |
| --- | --- |
| Currency is `eur` and amount > €500 | Prefer acquirer A (lower interchange) |
| Card is American Express | Always use Amex direct |
| Customer is on a soft-decline retry | Allow up to 2 alternate acquirers |
| Merchant category is `subscription` | Skip retry on `do_not_honor` (avoid issuer annoyance) |
## How approvals improve
The biggest gains come from three sources:
<details>
<summary>Per-card-type acquirer affinity</summary>
Some acquirers have stronger relationships with certain issuers — they share more authorization data, get better fraud signals back, and approve more cards as a result. Smart routing picks the acquirer that's been winning the most lately for cards that look like the one in front of it.
</details>
<details>
<summary>Network token usage</summary>
When the card has a network token available (most US cards do), routing through an acquirer that supports network tokens lifts approval rates by ~1.5% on average. Smart routing takes this into account.
</details>
<details>
<summary>Soft-decline retry</summary>
About 0.4% of declines are "soft" — the issuer's auth system was momentarily unavailable, the network had a glitch, or the message was malformed somewhere in the chain. Smart routing retries those through a different acquirer within a few hundred milliseconds, often turning a decline into an approval before the customer notices.
</details>
## Watching it work
The dashboard shows the route Evolve picked for each payment on the timeline:
<figure><img src="../.gitbook/assets/payment-routing-timeline.png" alt="A payment timeline showing Smart routing picked Acquirer A, with a backup retry on Acquirer B"><figcaption><p>The routing decision and any retries appear in the payment timeline.</p></figcaption></figure>
For aggregate visibility, the **Routing report** under **Reports → Routing** shows per-acquirer approval rates over time, so you can see the lift from smart routing against your previous baseline.
## What it doesn't do
* Smart routing isn't a price-shopping engine. It optimizes for approval rate, not interchange. If you want to bias toward lower-interchange acquirers, configure that explicitly in the rules.
* It won't paper over a real decline. `insufficient_funds` and `card_declined` for fraud are honored as-is.
* It doesn't change which payment methods are offered. That's still configured under [Payment methods](../concepts/payment-methods.md).
## Related
* [Failover and retries](failover.md) — broader resilience to acquirer outages. *Enterprise.*
* [3-D Secure and SCA](3d-secure.md) — pairing 3DS with the most likely-to-approve route.
* [Routing report](../reporting/standard-reports.md#routing-report) — measuring the lift.
references/example-site/products/payments/accept-payments/take-a-payment.md
---
icon: circle-dollar-to-slot
description: The four ways to accept a payment, and which one is right for your situation.
---
# Take a payment
Evolve gives you four channels to accept payments. They share the same backend — the same fees, the same settlement, the same dashboard. The differences are in how much engineering work the integration needs and how much control you have over the customer experience.
## Pick a channel
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-link" style="color:$primary;">:link:</i></h3></td><td><strong>Payment links</strong></td><td>One URL per payment or product. No code at all.</td><td></td></tr><tr><td><h3><i class="fa-window-maximize" style="color:$primary;">:window-maximize:</i></h3></td><td><strong>Hosted Checkout</strong></td><td>Redirect to an Evolve-hosted checkout page. ~10 lines of code.</td><td></td></tr><tr><td><h3><i class="fa-puzzle-piece" style="color:$primary;">:puzzle-piece:</i></h3></td><td><strong>Embedded Elements</strong></td><td>Drop-in card and bank fields in your own page. Custom UI.</td><td></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>Direct API</strong></td><td>Full control. PCI scope is your responsibility.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api">README.md</a></td></tr></tbody></table>
## When to use which
| Channel | Best for | Effort | Customization |
| --- | --- | --- | --- |
| **Payment links** | One-off invoices, social-media sales, pre-orders, donations | None | Logo and colors |
| **Hosted Checkout** | E-commerce checkouts, SaaS sign-ups, marketplaces | Low | Theme + custom fields |
| **Embedded Elements** | Branded checkout flows where the URL must stay yours | Medium | Full UI control around our fields |
| **Direct API** | Mobile apps, custom POS, complex marketplaces | High | Total |
{% if visitor.claims.unsigned.persona === "prospect" %}
{% hint style="info" icon="store" %}
**Selling on Shopify or another commerce platform?** You won't need any of these directly — install [Evolve for Shopify](https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/shopify) and the platform handles the channel choice for you.
{% endhint %}
{% endif %}
## Walkthrough: payment links
The lowest-friction option, and the one most teams use to test Evolve before committing to a deeper integration.
{% stepper %}
{% step %}
### Create the link
In **Payments → Payment links**, click **New link**. Set an amount, a description, and (optionally) a redirect URL for after payment. Click **Create**.
{% endstep %}
{% step %}
### Share it
Copy the link or download a QR code. Send it by email, paste it into a message, or embed it on your site.
{% endstep %}
{% step %}
### Watch it complete
When the customer pays, the new payment appears in **All payments** and (if you set one) the redirect URL fires.
{% endstep %}
{% endstepper %}
## Walkthrough: hosted Checkout
A redirect-based checkout you trigger from your site. You create a Checkout session on your server, redirect the customer, and Evolve sends them back when they're done.
For the integration steps, see the [Hosted Checkout guide](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api/checkout).
## Walkthrough: embedded Elements
Drop-in card and bank-account fields you can place inside your own page. The fields render in an iframe so the card data never touches your server — your PCI scope stays at SAQ A.
For the integration steps, see the [Elements guide](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api/elements).
## What happens after a payment
The lifecycle is the same regardless of which channel you used:
{% columns %}
{% column width="60%" %}
* The payment appears in **All payments** within seconds.
* Funds are added to your balance and included in the next [settlement](../reconciliation/settlement-files.md).
* If you've added webhook endpoints, a `payment.succeeded` event fires.
* The customer receives an email receipt (configurable in **Settings → Receipts**).
{% endcolumn %}
{% column %}
<a href="../concepts/payment-lifecycle.md" class="button secondary">See the full lifecycle</a>
{% endcolumn %}
{% endcolumns %}
references/example-site/products/payments/concepts/fees-and-pricing.md
---
icon: receipt
description: What each plan costs, what's included, and how fees show up in your settlements.
---
# Fees and pricing
Evolve's pricing has three components: a per-transaction fee, a small set of feature add-ons, and an optional same-day payout fee. There are no monthly minimums, no setup fees, and no card-network surcharges — what's listed here is what you pay.
{% if visitor.claims.unsigned.plan %}
## Your plan
{% endif %}
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="info" icon="layer-group" %}
**You're on Starter.** Card payments, USD only, T+3 payouts. Volume cap is **$50,000/month** — you'll get an email when you cross 80%, and we'll automatically suggest a Growth upgrade.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="layer-group" %}
**You're on Growth.** Cards, ACH, debit, and four-currency support. T+2 payouts. Volume cap is **$1,000,000/month**.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="layer-group" %}
**You're on Enterprise.** Custom pricing, all rails, all currencies, T+1 default with same-day available. Volume is uncapped.
{% endhint %}
{% endif %}
## Plan comparison
| | Starter | Growth | Enterprise |
| --- | --- | --- | --- |
| **Card (domestic)** | 2.9% + $0.30 | 2.7% + $0.30 | Custom (interchange-plus) |
| **Card (international)** | +1.5% | +1.0% | Custom |
| **ACH debit** | — | 0.8%, capped at $5 | Custom |
| **Wire transfer** | — | — | $20 flat |
| **SEPA / BACS** | — | — | Custom |
| **Currency conversion** | — | Wholesale + 0.5% | Wholesale + negotiated |
| **Dispute fee** | $15 | $15 | Negotiated |
| **Refund** | Free (network fee not returned) | Free (network fee not returned) | Free |
| **Same-day payouts** | — | — | +0.4% per transfer |
| **Monthly volume cap** | $50K | $1M | None |
| **Payout schedule** | T+3 | T+2 | T+1 (same-day available) |
## How fees appear in your settlement
Fees come out of your settlement, not your bank account — there's no separate invoice. Each settlement file has a per-payment breakdown:
| Column | Example |
| --- | --- |
| `gross_amount` | `42.00` |
| `processing_fee` | `-1.52` |
| `net_amount` | `40.48` |
Refunds and dispute fees show up as separate negative line items in the same file. See [Settlement files](../reconciliation/settlement-files.md) for the full schema.
## Add-ons
A few features carry their own pricing on top of your plan:
<details>
<summary>Smart routing (Growth and Enterprise)</summary>
Included on Growth. On Enterprise it's also included by default but can be configured per acquirer agreement. See [Smart routing](../accept-payments/smart-routing.md).
</details>
<details>
<summary>3-D Secure 2 (all plans)</summary>
Free for required transactions (e.g. EU SCA). Optional 3DS on non-required transactions costs $0.05 per attempt — usually worth it for the liability shift on high-value charges.
</details>
<details>
<summary>Same-day payouts (Enterprise only)</summary>
0.4% of each same-day payout, billed against the same payout. You can leave the default T+1 schedule and use same-day on demand from the dashboard.
</details>
<details>
<summary>Custom reports (Growth and Enterprise)</summary>
Included up to 50 saved reports per workspace. Above that, $50/month per additional 50. See [Custom reports](../reporting/custom-reports.md).
</details>
## Promised pricing
For Enterprise customers on a custom contract, your negotiated rates are the source of truth. The table above shows the standard published pricing — if your contract overrides any line, what's in the contract applies. You can always see your effective rates in **Settings → Billing**.
{% if visitor.claims.unsigned.plan === "enterprise" %}
<p><a href="https://gitbook.com" class="button primary">View your contracted rates</a></p>
{% endif %}
references/example-site/products/payments/concepts/money-movement.md
---
icon: money-bill-transfer
description: How captured funds become a payout in your bank account.
---
# Money movement and settlement
A captured charge isn't yet money in your bank — it's a balance on your Evolve account. Evolve groups captured charges into daily settlements and pushes the net amount to your linked bank account on a payout schedule that depends on your plan.
## The flow
```mermaid
flowchart LR
A[Captured] --> B[Available balance]
B --> C[Daily settlement]
C --> D[Payout to bank]
```
* **Available balance** updates in real time. You can see it in the dashboard or query `GET /v1/balance`.
* **Settlement** runs once a day at <code class="expression">space.vars.settlement_time_utc</code>. It bundles all captures, refunds, fees, and dispute deductions into a single net amount and produces a [settlement file](../reconciliation/settlement-files.md).
* **Payouts** are initiated from the settlement file on your plan's schedule.
## Payout schedule by plan
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="info" icon="clock" %}
**Starter:** Payouts arrive **T+3 business days** after capture. There's no faster option on this plan.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="clock" %}
**Growth:** Payouts arrive **T+2 business days** after capture. Same-day payouts are available as an Enterprise add-on.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="clock" %}
**Enterprise:** Default schedule is **T+1 business day**. Same-day payouts are available for an additional 0.4% per transfer — turn it on under **Settings → Payouts**.
{% endhint %}
{% endif %}
| Plan | Default schedule | Faster option |
| --- | --- | --- |
| Starter | T+3 | — |
| Growth | T+2 | — |
| Enterprise | T+1 | Same-day (+0.4%) |
## What's deducted
Each settlement nets the following against your captures:
* **Processing fees** — per your plan's pricing.
* **Refunds issued in that window**.
* **Dispute deductions** — both the disputed amount and the per-dispute fee ($15 USD).
* **Reserves**, if your account has a reserve policy in place.
The settlement file shows each line item by ID so you can match it back to the originating charge or refund. See [Settlement files](../reconciliation/settlement-files.md).
## Multi-currency
If you accept multiple currencies, Evolve maintains a separate balance and payout schedule per currency. You can hold balances and pay out to bank accounts denominated in the same currency, or convert at settlement time at the daily wholesale rate plus 0.5%.
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="info" %}
Enterprise customers can negotiate the FX margin and configure per-entity payout accounts. Contact your account team.
{% endhint %}
{% endif %}
## Holds and reserves
Two scenarios delay payout for individual charges:
<details>
<summary>Risk holds</summary>
If a charge triggers a risk rule, Evolve may hold the funds for up to 14 days while the transaction is reviewed. You'll see `held_for_review: true` on the charge object and a `payout_hold` line in the settlement file.
</details>
<details>
<summary>Account reserves</summary>
For new accounts or those processing high-risk verticals, Evolve may set a rolling reserve — typically 5% held for 90 days. Reserves release automatically as they age out and appear as positive line items on later settlements.
</details>
## Related
* [Settlement files](../reconciliation/settlement-files.md) — the daily CSV format.
* [Disputes and chargebacks](../reconciliation/disputes.md) — how disputes affect settlement.
* [Reporting](../reporting/README.md) — building finance reports on top of settlements.
references/example-site/products/payments/concepts/payment-lifecycle.md
---
icon: arrows-rotate
description: The states a payment moves through, and how the dashboard surfaces each one.
---
# Payment lifecycle
Every payment in Evolve moves through a predictable sequence of states. The dashboard shows the current state as a badge on the payment row, and the full state history on the timeline view.
The most important distinction to understand is between **authorized** (the issuer has approved the charge but funds are only on hold) and **captured** (funds are moving to your account). Most charges authorize and capture in one step — but for pre-orders, marketplaces, and hospitality holds, you can split them.
## The states
```mermaid
flowchart LR
Pending --> Authorized --> Captured
Pending -.->|declined| Failed
Authorized -.->|voided| Voided
Captured -.->|refund| Refunded
Captured -.->|disputed| Disputed
Disputed -.-> Won
Disputed -.-> Lost
```
| State | What it means | Funds impact | Dashboard badge |
| --- | --- | --- | --- |
| Pending | Sent to the network, waiting on issuer response. | None yet. | Grey |
| Authorized | Issuer approved; funds are held, not moved. | Customer's available balance is reduced. | Yellow |
| Captured | The hold has been settled — funds are moving to you. | Counts toward your next [payout](money-movement.md). | Green |
| Failed | Issuer declined. The decline reason is on the timeline. | None. | Red |
| Voided | Authorization released before capture. | Hold reversed. | Grey |
| Refunded | Captured funds returned to the customer. | Reduces your next payout. | Blue |
| Disputed | Cardholder filed a chargeback. | Funds withheld pending outcome. | Orange |
## Authorize-then-capture
By default, payments authorize and capture in a single step — the moment the customer confirms, the funds move. For some businesses that's not what you want:
* **Pre-orders** — authorize when the order is placed, capture when you ship.
* **Marketplaces** — authorize the buyer, capture once the seller fulfills.
* **Hospitality** — authorize an estimated total at check-in, capture the actual amount at check-out.
You can switch any payment link, Checkout session, or API call to two-step by toggling **Capture: manual**. You then have **up to 7 days** to capture before the authorization expires and the hold releases automatically.
{% hint style="warning" %}
Authorizations expire silently. There's no email or webhook for an expired auth. If you rely on long holds, set a reminder on your side to capture within the window.
{% endhint %}
## Watching state changes
Three places to track lifecycle events:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-list-timeline" style="color:$primary;">:list-timeline:</i></h3></td><td><strong>Dashboard timeline</strong></td><td>Visual history per payment.</td><td></td></tr><tr><td><h3><i class="fa-bolt" style="color:$primary;">:bolt:</i></h3></td><td><strong>Webhooks</strong></td><td>Push events to your own systems.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks">README.md</a></td></tr><tr><td><h3><i class="fa-chart-bar" style="color:$primary;">:chart-bar:</i></h3></td><td><strong>Reports</strong></td><td>Aggregate state counts over time.</td><td><a href="../reporting/standard-reports.md">standard-reports.md</a></td></tr></tbody></table>
## Related
* [Payment methods](payment-methods.md) — what each method supports.
* [Money movement and settlement](money-movement.md) — when captured funds become a payout.
* [Refunds](../reconciliation/refunds.md) — how refunds appear in the lifecycle.
references/example-site/products/payments/concepts/payment-methods.md
---
icon: wallet
description: Which payment methods Evolve supports, and which ones are available on your plan.
---
# Payment methods
Evolve supports cards, bank rails, and direct-debit schemes. The methods available to you depend on your plan and on the destination currency.
## Methods by plan
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="info" icon="layer-group" %}
**You're on Starter.** Card payments only, in USD. To accept ACH, debit, or international rails, [upgrade to Growth](https://gitbook.com).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="layer-group" %}
**You're on Growth.** Cards, ACH, and debit are enabled. Wires, SEPA, and BACS are Enterprise features — talk to your account team if you need them.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="layer-group" %}
**You're on Enterprise.** All methods listed below are enabled, including international rails. Multi-currency settlement is on by default.
{% endhint %}
{% endif %}
| Method | Starter | Growth | Enterprise | Settlement |
| --- | :---: | :---: | :---: | --- |
| Card (Visa, Mastercard, Amex, Discover) | ✅ | ✅ | ✅ | T+1–3 |
| Card (JCB, UnionPay, Diners) | — | ✅ | ✅ | T+2–3 |
| ACH debit (US) | — | ✅ | ✅ | T+3 (initial), T+1 (verified) |
| Debit card (US) | ✅ | ✅ | ✅ | T+1–2 |
| Wire transfer (US) | — | — | ✅ | Same day |
| SEPA (EU) | — | — | ✅ | T+1 |
| BACS Direct Debit (UK) | — | — | ✅ | T+3 |
| Multi-currency (USD, CAD, GBP, EUR) | — | ✅ | ✅ | Per-currency payout |
| Multi-currency (135 currencies) | — | — | ✅ | Per-currency payout |
## Choosing which methods to offer
You decide which methods to offer on each payment. In the dashboard, **Settings → Payment methods** sets the default set for new payment links and Checkout sessions. You can override per session — for example, if you want a B2B invoice to accept wire only.
<figure><img src="../.gitbook/assets/payment-methods-settings.png" alt="The Payment methods settings panel in the Evolve dashboard"><figcaption><p>Toggle the methods you want to accept by default.</p></figcaption></figure>
## Risk considerations
Different methods carry different risk profiles:
<details>
<summary>Cards</summary>
Highest authorization rate, highest dispute exposure. Chargebacks can land up to 120 days after the charge. Use [3-D Secure](../accept-payments/3d-secure.md) on high-value transactions to shift liability.
</details>
<details>
<summary>ACH debit</summary>
Lower fees, but reversal risk lasts 60 days for consumer accounts. Verify the account before charging large amounts — Evolve supports instant verification via Plaid (see [Identity / Bank verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/bank-account)).
</details>
<details>
<summary>Wire transfer</summary>
Same-day finality, no reversals. Best for high-value B2B. Customer pushes funds to a unique virtual account number; Evolve matches them to your charge by reference code.
</details>
## Related
* [Money movement and settlement](money-movement.md) — when each method's funds arrive in your account.
* [3-D Secure and SCA](../accept-payments/3d-secure.md) — when to require additional cardholder authentication.
* [Identity / Bank verification](https://app.gitbook.com/s/w7NRnYZuokE4h1mm2pJB/verification-flows/bank-account) — verifying ACH accounts before debit.
references/example-site/products/payments/quickstart/accept-your-first-payment.md
---
icon: rocket
description: Take your first test payment from the dashboard — no integration required.
---
# Accept your first payment
The fastest way to see Evolve work is to send a payment link, pay it yourself with a test card, and watch it land in your dashboard. You don't need to write any code.
{% hint style="info" %}
This walkthrough uses test mode. Test charges are real, but no money moves and nothing leaves the test environment.
{% endhint %}
{% stepper %}
{% step %}
### Open the test dashboard
Sign in to <a href="https://gitbook.com"><code class="expression">space.vars.dashboard_test</code></a>. If you've just created your account, you'll land in test mode by default — you can tell by the **Test** badge in the top bar.
<figure><img src="../.gitbook/assets/dashboard-test-badge.png" alt="The Evolve dashboard showing a Test mode badge in the header"><figcaption><p>Look for the Test badge in the top bar.</p></figcaption></figure>
{% endstep %}
{% step %}
### Create a payment link
Go to **Payments → Payment links** and click **New link**. Set:
* **Amount** — `$42.00`
* **Description** — `First test payment`
* **Methods** — leave the defaults
Click **Create**. Evolve generates a hosted checkout URL you can share with anyone — no integration on your side.
{% endstep %}
<figure><img src="../.gitbook/assets/payment-link-created.png" alt="A created payment link with a Copy button"><figcaption></figcaption></figure>
{% step %}
### Pay it with a test card
Open the payment link in a new tab. On the hosted checkout, use one of these test cards:
| Card | Number | Result |
| --- | --- | --- |
| Visa | `4242 4242 4242 4242` | Approved |
| Visa | `4000 0000 0000 0002` | Declined (`card_declined`) |
| Mastercard | `5555 5555 5555 4444` | Approved |
| Visa (3-D Secure required) | `4000 0027 6000 3184` | Approved after authentication |
Use any future expiry date and any 3-digit CVC.
{% endstep %}
{% step %}
### See it land in the dashboard
Back in the dashboard, the new charge appears at the top of **Payments → All payments**. Click it to see the full timeline — when the link was opened, when the card was approved, and when the funds were captured.
{% endstep %}
{% step %}
### Refund it
From the charge page, click **Refund**. You can refund the full amount or a partial amount. The refund appears as a separate row in the timeline and as a deduction on the next [settlement file](../reconciliation/settlement-files.md).
{% endstep %}
{% endstepper %}
## What's next?
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-toggle-on" style="color:$primary;">:toggle-on:</i></h3></td><td><strong>Switch to live mode</strong></td><td>What changes when you flip the switch.</td><td><a href="test-and-live-mode.md">test-and-live-mode.md</a></td></tr><tr><td><h3><i class="fa-circle-dollar-to-slot" style="color:$primary;">:circle-dollar-to-slot:</i></h3></td><td><strong>Take payments at scale</strong></td><td>Beyond payment links: Checkout, Elements, the API.</td><td><a href="../accept-payments/take-a-payment.md">take-a-payment.md</a></td></tr><tr><td><h3><i class="fa-bolt" style="color:$primary;">:bolt:</i></h3></td><td><strong>Set up a webhook</strong></td><td>React to payment events automatically.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/webhooks">README.md</a></td></tr></tbody></table>
{% hint style="info" %}
**Building an integration?** Developers / [Payments API quickstart](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api) covers the same flow with code samples in cURL, Node, Python, Go, and Ruby.
<p><button type="button" class="button primary" data-action="ask" data-query="How do I integrate Evolve Payments with my own checkout?" data-icon="code">Ask the docs</button></p>
{% endhint %}
references/example-site/products/payments/quickstart/test-and-live-mode.md
---
icon: flask
description: Two separate environments — what changes between them, and how to flip safely.
---
# Test mode and live mode
{% include "../.gitbook/includes/environments.md" %}
## What's different in live mode
* **Real money moves.** Successful charges debit the cardholder and are scheduled for payout to your bank account. See [Money movement](../concepts/money-movement.md) for payout timing.
* **Real cards only.** Test card numbers (`4242 4242 4242 4242` and friends) are rejected with `card_declined` in live mode.
* **Webhooks fire to your live endpoint.** Make sure your verification logic is using the live signing secret (it's a separate value, prefixed `whsec_live_`).
* **Settlement files are generated.** A new CSV lands in your reconciliation feed every day at 06:00 UTC. See [Settlement files](../reconciliation/settlement-files.md).
## Flipping from test to live
There is no merge or migration step — test and live are fully separate. To go live:
1. Complete the dashboard onboarding (business verification, bank account, statement descriptor).
2. Generate a live key under **Developers → API keys**.
3. Swap `sk_test_*` for `sk_live_*` in your environment configuration.
4. Update webhook endpoints to point at your production URL and use the live signing secret.
{% hint style="warning" %}
**Don't rely on switching keys at runtime.** Hard-code the environment in your config, not by inspecting the key prefix. Code that branches on key shape tends to leak test behavior into production.
{% endhint %}
## Cutting over from a test integration
If you've been integrating against test mode, the fastest path to confidence in live is:
<details>
<summary>Run a single small live charge end to end</summary>
Use a real card you control, charge $1.00, confirm it appears in your dashboard, and refund it. This proves your live key, webhook signature, and settlement view are wired up correctly — without putting any customer-facing flow at risk.
</details>
<details>
<summary>Verify your webhook handler against live signatures</summary>
Test-mode webhooks are signed with a different secret than live-mode webhooks. A common pre-launch bug is leaving the test signing secret in your handler. Replay a live webhook from the dashboard's **Webhook log** and confirm your handler accepts it.
</details>
<details>
<summary>Reconcile your first settlement</summary>
After your first live payout, download the settlement CSV from the dashboard and walk through it row by row. See [Settlement files](../reconciliation/settlement-files.md) for the schema and a sample reconciliation script.
</details>
references/example-site/products/payments/README.md
---
description: Route, reconcile, and report on every payment.
icon: credit-card
cover: .gitbook/assets/payments-cover.png
coverY: 0
layout:
width: wide
cover:
visible: true
size: full
title:
visible: true
description:
visible: true
tableOfContents:
visible: true
outline:
visible: true
pagination:
visible: true
metadata:
visible: true
tags:
visible: true
---
# Payments
{% columns %}
{% column %}
Evolve Payments handles the work between a customer's "pay" click and the money landing in your account — card and bank-rail processing, network routing, 3-D Secure, settlement, and reporting. This space covers everything from the first test charge to enterprise routing rules.
<button type="button" class="button primary" data-action="ask" data-icon="gitbook-assistant">Ask the Evolve docs</button>
<button type="button" class="button secondary" data-action="ask" data-query="How do I accept my first payment?" data-icon="rocket">First payment</button> <button type="button" class="button secondary" data-action="ask" data-query="When do I need 3-D Secure?" data-icon="shield-halved">3-D Secure</button> <button type="button" class="button secondary" data-action="ask" data-query="How do I route around card declines?" data-icon="route">Smart routing</button>
{% endcolumn %}
{% column %}
{% hint style="success" icon="gitbook" %}
**A note from GitBook**
This space is the deepest in the demo and shows off three GitBook features: **adaptive content** (hints and entire blocks change with the visitor), **space variables** (URLs and constants pulled from `vars.yaml`), and **synced blocks** (the test/live environments table is reused across all three product spaces).
{% if !visitor.claims.unsigned.persona %}
Try a persona to see adaptive content in action across the site:
<a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="seedling">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona %}
<i class="fa-id-card-clip" style="color:$info;">:id-card-clip:</i> You are currently <code class="expression">visitor.claims.unsigned.persona === "prospect" ? "a prospect user exploring the product" : visitor.claims.unsigned.persona === "new" ? "a new user" : visitor.claims.unsigned.persona === "existing" ? "an existing user" : visitor.claims.unsigned.persona === "partner" ? "a partner" : ""</code><code class="expression">visitor.claims.unsigned.plan ? " on the " + visitor.claims.unsigned.plan.charAt(0).toUpperCase() + visitor.claims.unsigned.plan.slice(1) + " plan" : ""</code>. [<mark style="color:$primary;">Reset</mark>](https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=)
{% endif %}
{% if visitor.claims.unsigned.persona === "prospect" %}
<a class="button primary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a class="button primary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=partner&plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=new&visitor.plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a class="button primary" data-icon="rocket">Migrator</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=partner&visitor.plan=enterprise" class="button secondary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
<a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=prospect" class="button secondary" data-icon="bag-shopping">Prospect</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=new&plan=starter" class="button secondary" data-icon="arrow-right-to-bracket">New user</a> <a href="https://enterprise-demos.gitbook.io/evolve-docs/payments?visitor.persona=existing&visitor.plan=growth" class="button secondary" data-icon="rocket">Migrator</a> <a class="button primary" data-icon="handshake-angle">Partner</a>
{% endif %}
{% endhint %}
{% endcolumn %}
{% endcolumns %}
***
## <i class="fa-sparkle" style="color:$info;">:sparkle:</i> Picked for you
{% if visitor.claims.unsigned.persona === "prospect" %}
{% hint style="info" icon="store" %}
**Evaluating Evolve?** Get up and running with Payments with the resources below.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><h3><strong>Accept your first payment</strong></h3></td><td>Take your first test payment in five minutes.</td></tr><tr><td><h3><i class="fa-receipt" style="color:$primary;">:receipt:</i></h3></td><td><h3><strong>Fees and pricing</strong></h3></td><td>What each plan costs, what's included, and how fees show up in your settlements.</td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
{% hint style="info" icon="hand-wave" %}
**New to Evolve Payments?** The dashboard works on its own — get a real test charge in five minutes, no code required.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><h3><strong>Accept your first payment</strong></h3></td><td>Take your first test payment in five minutes — no integration required.</td><td><a href="quickstart/accept-your-first-payment.md">accept-your-first-payment</a></td></tr><tr><td><h3><i class="fa-flask" style="color:$primary;">:flask:</i></h3></td><td><h3><strong>Test mode and live mode</strong></h3></td><td>What changes when you flip from test to live, and the cutover checklist.</td><td><a href="quickstart/test-and-live-mode.md">test-and-live-mode</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "existing" %}
{% hint style="info" icon="arrows-left-right" %}
**Migrating from Stripe?** Most APIs map cleanly. Start with the migration guide and the saved-method patterns.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-arrows-left-right" style="color:$primary;">:arrows-left-right:</i></h3></td><td><h3><strong>Migrate from Stripe</strong></h3></td><td>Field mapping, parallel-run pattern, and the cutover checklist.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/migrate-from-stripe">migrate-from-stripe</a></td></tr><tr><td><h3><i class="fa-bookmark" style="color:$primary;">:bookmark:</i></h3></td><td><h3><strong>Save cards for repeat customers</strong></h3></td><td>How Stripe's Customer/PaymentMethod model maps to Evolve.</td><td><a href="https://app.gitbook.com/s/Nankrp40VchJsUblU6h6/payment-flows/save-cards">save-cards</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona === "partner" %}
{% hint style="info" icon="building" %}
**Setting up enterprise features?** Smart routing, failover, and same-day payouts are the highest-leverage Enterprise capabilities.
{% endhint %}
<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-route" style="color:$primary;">:route:</i></h3></td><td><h3><strong>Smart routing</strong></h3></td><td>Per-network success-rate optimization. 1–3% lift in approval rate.</td><td><a href="accept-payments/smart-routing.md">smart-routing</a></td></tr><tr><td><h3><i class="fa-arrows-spin" style="color:$primary;">:arrows-spin:</i></h3></td><td><h3><strong>Failover and retries</strong></h3></td><td>Stay up when an acquirer or network goes down.</td><td><a href="accept-payments/failover.md">failover</a></td></tr></tbody></table>
{% endif %}
{% if visitor.claims.unsigned.persona %}
***
{% endif %}
{% if !visitor.claims.unsigned.persona %}
## Get started
{% endif %}
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-rocket" style="color:$primary;">:rocket:</i></h3></td><td><strong>Quickstart</strong></td><td>Take your first test payment in five minutes.</td><td><a href="quickstart/accept-your-first-payment.md">accept-your-first-payment.md</a></td></tr><tr><td><h3><i class="fa-book-open" style="color:$primary;">:book-open:</i></h3></td><td><strong>Concepts</strong></td><td>How payments move through Evolve.</td><td><a href="concepts/payment-lifecycle.md">payment-lifecycle.md</a></td></tr><tr><td><h3><i class="fa-bolt" style="color:$primary;">:bolt:</i></h3></td><td><strong>Accept payments</strong></td><td>Charges, saved methods, 3-D Secure, routing.</td><td><a href="accept-payments/">accept-payments</a></td></tr><tr><td><h3><i class="fa-scale-balanced" style="color:$primary;">:scale-balanced:</i></h3></td><td><strong>Reconciliation</strong></td><td>Settlement files, refunds, disputes.</td><td><a href="reconciliation/">reconciliation</a></td></tr><tr><td><h3><i class="fa-chart-line" style="color:$primary;">:chart-line:</i></h3></td><td><strong>Reporting</strong></td><td>Daily reports, exports, and finance pushes.</td><td><a href="reporting/">reporting</a></td></tr><tr><td><h3><i class="fa-code" style="color:$primary;">:code:</i></h3></td><td><strong>API reference</strong></td><td>Endpoints, SDKs, and try-it.</td><td><a href="https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api/">payments-api</a></td></tr></tbody></table>
## What's new
The biggest recent shipments — see the full [changelog](https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ErQsbFsgm6eg9BApdmPl/) for the back-catalog.
* **Smart routing v2** — per-network success-rate optimization, available on Growth and Enterprise. [Read more](accept-payments/smart-routing.md).
* **Same-day payouts** — opt-in for Enterprise customers in the US. [Read more](concepts/money-movement.md).
* **Disputes API** — programmatic evidence submission, replacing the legacy CSV upload. [Read more](reconciliation/disputes.md).
## Get help
{% columns %}
{% column width="50%" %}
#### Talk to support
For account-specific questions, billing, or production incidents, contact your account team or open a ticket from the dashboard.
<a href="https://gitbook.com" class="button primary">Open a ticket</a>
{% endcolumn %}
{% column width="50%" %}
#### Search the docs
Looking for something specific? The Assistant pulls answers from this site, the API reference, and the community forum.
<button type="button" class="button secondary" data-action="search" data-icon="magnifying-glass">Search...</button>
{% endcolumn %}
{% endcolumns %}
references/example-site/products/payments/reconciliation/disputes.md
---
icon: gavel
description: When a customer disputes a charge, what happens, and how to fight back.
---
# Disputes and chargebacks
A dispute (or chargeback) is when a cardholder asks their bank to reverse a charge. The bank pulls the funds back from your account immediately and gives you a window to submit evidence that the charge was legitimate. Win, and the funds come back. Lose, and the funds stay with the cardholder along with a $15 dispute fee.
Disputes are the slowest, most expensive part of taking card payments. The good news is that most disputes are preventable, and many of the rest are winnable if you respond quickly.
## The lifecycle
```mermaid
flowchart LR
A[Cardholder<br>contacts bank] --> B[Bank withdraws<br>disputed amount]
B --> C[Evolve creates<br>dispute record]
C --> D{You respond<br>within window?}
D -->|Yes| E[Submit evidence]
D -->|No| F[Forfeit by default]
E --> G{Network reviews}
G -->|Won| H[Funds returned<br>fee not refunded]
G -->|Lost| I[Funds and fee retained<br>by cardholder]
F --> I
```
## Dispute types
Not every dispute is a fraud claim. The reason code on the dispute matters — it tells you what evidence to gather:
| Type | Reason code examples | What the cardholder is claiming |
| --- | --- | --- |
| **Fraud** | `fraudulent`, `unrecognized` | They didn't make the charge |
| **Service** | `product_not_received`, `service_not_provided` | You didn't deliver |
| **Quality** | `defective`, `not_as_described` | What you delivered wasn't right |
| **Processing** | `duplicate`, `credit_not_processed` | Something went wrong with the transaction itself |
| **Authorization** | `general` | A catch-all the bank uses when the cardholder's reason doesn't fit elsewhere |
## Response window
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="info" %}
**Enterprise customers** can configure a dispute alerts integration with Verifi or Ethoca. When enabled, you can resolve a fraud claim by issuing a pre-emptive refund within 72 hours and avoid the chargeback entirely. Talk to your account team to set this up.
{% endhint %}
{% endif %}
You have **20 calendar days** from the dispute notification to submit evidence. After that, the network closes the case in the cardholder's favor by default.
The dashboard shows the deadline prominently on every open dispute, and we'll send three reminder emails — at 7 days, 3 days, and 1 day remaining.
## Responding from the dashboard
{% stepper %}
{% step %}
### Open the dispute
Disputes appear in **Reconciliation → Disputes**. Open one to see the disputed payment, the reason code, the cardholder's stated complaint (if available), and the response form.
{% endstep %}
{% step %}
### Decide: fight or accept
Two options:
* **Submit evidence** — you believe the charge was legitimate and you can prove it.
* **Accept the dispute** — you agree the cardholder is right (or it's not worth fighting). The funds stay with them, the fee stays with you. Often the right call for low-value disputes.
{% endstep %}
{% step %}
### Gather evidence
The form lists the evidence types most likely to win this reason code. For a `product_not_received` dispute on a physical product, that's typically:
* Shipping carrier and tracking number.
* Proof of delivery (signature, photo, or driver attestation).
* The order confirmation email sent to the cardholder.
* A copy of your refund and shipping policies.
For a digital product or service, it's different — IP address logs, login history, communication records.
{% endstep %}
{% step %}
### Submit
Once you submit, the response is locked. You can attach up to 10 files (PDF, PNG, JPG) and 5,000 characters of text. The network reviews in 30–75 days.
{% endstep %}
{% endstepper %}
## Win rates by reason code
These are rough Evolve-wide averages — your numbers will depend on your evidence quality and product type.
| Reason code | Avg. win rate |
| --- | --- |
| `product_not_received` (with tracking) | 65% |
| `defective` / `not_as_described` | 30% |
| `duplicate` | 80% |
| `fraudulent` (with 3DS) | 45% |
| `fraudulent` (without 3DS) | 12% |
| `general` | 25% |
The biggest lever is [3-D Secure](../accept-payments/3d-secure.md) for fraud disputes — the liability shift means you almost always win when 3DS was used and the issuer authenticated the cardholder.
## Preventing disputes
Most disputes are preventable. The single most effective preventions:
<details>
<summary>Clear billing descriptors</summary>
The descriptor that appears on the cardholder's statement should be the brand name they recognize. "EVOLVE*ACME-CO" is recognizable; "MERCH 84920" is not. Set yours in **Settings → Billing → Statement descriptor**.
</details>
<details>
<summary>Easy refund flow</summary>
Most "fraud" disputes are actually friendly fraud — the cardholder didn't recognize the charge or couldn't figure out how to get a refund, so they called their bank. A visible, low-friction refund path on your site or in your receipts heads off most of these.
</details>
<details>
<summary>Proactive fraud prevention</summary>
Card-testing attacks (small charges from many fresh cards) generate fraud disputes weeks later. Evolve's risk rules block most of these automatically; check the Risk dashboard to see what's been blocked.
</details>
<details>
<summary>3-D Secure on high-value charges</summary>
For amounts over $500, the fraud-dispute math usually favors requiring 3DS — even with 1–2% checkout abandonment, the liability shift on disputes pays for itself.
</details>
## How disputes appear in your finances
The disputed amount is withdrawn from your balance the moment the dispute opens, not when it resolves. On the settlement file:
* **Dispute opens:** a `dispute_lost` row provisionally deducts the disputed amount + $15 fee.
* **Dispute won:** a `dispute_won` row credits the disputed amount back. The $15 fee is not refunded.
* **Dispute lost:** no further action — the provisional deduction becomes final.
This means your balance shows the worst-case outcome from the moment a dispute opens. If you win, the funds come back on the settlement file for the day the network ruled.
## Related
* [Settlement files](settlement-files.md) — how disputes show up in your daily reconciliation.
* [3-D Secure and SCA](../accept-payments/3d-secure.md) — the most effective dispute prevention.
* [Reporting](../reporting/standard-reports.md#dispute-report) — dispute rates over time.
references/example-site/products/payments/reconciliation/README.md
---
description: Match every payment, refund, fee, and dispute against your bank account.
icon: scale-balanced
---
# Reconciliation
Reconciliation is the daily work of matching what's in your bank account against what should be there — payment by payment, fee by fee. Evolve's job is to make that work mechanical: every cent is accounted for, every line item is traceable to its origin, and the file you import into your accounting system always balances.
## What gets reconciled
Three things end up in your settlement, and all three need to balance:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-file-csv" style="color:$primary;">:file-csv:</i></h3></td><td><strong>Settlement files</strong></td><td>Daily CSV of every captured payment, fee, and adjustment.</td><td><a href="settlement-files.md">settlement-files.md</a></td></tr><tr><td><h3><i class="fa-rotate-left" style="color:$primary;">:rotate-left:</i></h3></td><td><strong>Refunds</strong></td><td>How refunds appear and reduce your payout.</td><td><a href="refunds.md">refunds.md</a></td></tr><tr><td><h3><i class="fa-gavel" style="color:$primary;">:gavel:</i></h3></td><td><strong>Disputes and chargebacks</strong></td><td>Withheld funds, evidence, and outcomes.</td><td><a href="disputes.md">disputes.md</a></td></tr></tbody></table>
## The daily rhythm
```mermaid
flowchart LR
A["Captures<br>(throughout the day)"] --> B["Settlement cut-off<br>06:00 UTC"]
B --> C[Settlement file generated]
C --> D[Payout initiated]
D --> E[Funds in your bank<br>per plan schedule]
```
Every day at <code class="expression">space.vars.settlement_time_utc</code>, Evolve closes the books on the previous business day and produces:
* A **settlement file** (CSV) listing every payment, refund, fee, and adjustment that contributed to that day's balance.
* A **payout** to your linked bank account, equal to the net amount on the file.
* A **balance update** in the dashboard reflecting the new available balance.
Your finance team's job each day is to take the settlement file, match it to the bank deposit, and book the per-payment fees and refunds into your accounting system. For most teams this is a 5-minute task.
## Who does what
{% if visitor.claims.unsigned.persona === "partner" %}
{% hint style="info" icon="building" %}
**Setting up Enterprise reconciliation?** You probably want **scheduled exports** to your data warehouse, not manual CSV downloads. See [Sharing and scheduled exports](../reporting/sharing-exports.md) for the SFTP and S3 push options.
{% endhint %}
{% endif %}
| Role | What they do |
| --- | --- |
| **Finance / accounting** | Imports settlement files, matches to bank, books journal entries. |
| **Ops / support** | Issues refunds, responds to dispute notifications. |
| **Engineering** | Configures webhooks, builds reporting integrations. |
| **Leadership** | Watches dispute rate and net revenue trends in [Reporting](../reporting/README.md). |
## Permissions
Reconciliation views and actions are gated by role:
* **Viewer** — see settlements, can't issue refunds or respond to disputes.
* **Finance** — see settlements, download files, can't issue refunds.
* **Ops** — issue refunds and respond to disputes.
* **Admin** — everything, plus permission management.
Configure roles in **Settings → Team → Roles**.
## Where this fits
Reconciliation is downstream of [Accept payments](../accept-payments/README.md) (where the captures originate) and upstream of [Reporting](../reporting/README.md) (where you build longer-horizon views). If you're building an integration with QuickBooks, NetSuite, or your own data warehouse, see [Guides / Integrations](https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/).
references/example-site/products/payments/reconciliation/refunds.md
---
icon: rotate-left
description: How to issue a refund, when funds reach the customer, and how it shows up on your books.
---
# Refunds
A refund returns funds to the original card or bank account the customer paid with. You can refund a payment in full or in part, as many times as you like up to the original amount, within your plan's refund window.
## Refund window by plan
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="info" icon="clock-rotate-left" %}
**You're on Starter** — you can refund a payment up to **60 days** after it was captured. After that, the only option is a manual ACH or wire from your own account.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "growth" %}
{% hint style="info" icon="clock-rotate-left" %}
**You're on Growth** — you can refund a payment up to **90 days** after it was captured. After that, the only option is a manual ACH or wire from your own account.
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.plan === "enterprise" %}
{% hint style="success" icon="clock-rotate-left" %}
**You're on Enterprise** — you can refund a payment up to **180 days** after it was captured. Beyond that, your account team can arrange a one-off out-of-band refund.
{% endhint %}
{% endif %}
| Plan | Refund window |
| --- | --- |
| Starter | 60 days |
| Growth | 90 days |
| Enterprise | 180 days |
The window is measured from the **capture** date, not the original authorization. For two-step payments, that means the clock starts ticking when you capture, not when the customer paid.
## Issuing a refund
{% stepper %}
{% step %}
### Open the payment
Find the payment in **Payments → All payments** and click into it.
{% endstep %}
{% step %}
### Click "Refund"
You'll see a dialog with the maximum refundable amount, a free-text reason field, and an optional internal note.
{% endstep %}
{% step %}
### Pick full or partial
Refund the full amount, or enter a smaller amount. You can issue multiple partial refunds against the same payment until you've returned the full original amount.
{% endstep %}
{% step %}
### Confirm
The refund appears on the payment timeline immediately as **Pending**, then updates to **Succeeded** once it's accepted by the network — usually within a minute for cards, several days for ACH.
{% endstep %}
{% endstepper %}
The customer receives an email confirmation (configurable in **Settings → Receipts**), and a `refund.succeeded` webhook fires if you've subscribed to it.
## When the customer sees the money
Refund timing depends on the original payment method:
| Method | Time to customer's account |
| --- | --- |
| Card (Visa, Mastercard) | 5–10 business days |
| Card (Amex) | 5–10 business days |
| Card (Discover) | 5–10 business days |
| ACH debit | 5–7 business days |
| SEPA | 1–3 business days |
| Wire | Same day |
These are the issuer's posting times, not Evolve's processing times. Evolve initiates the refund within minutes; the lag is the customer's bank.
## How it shows up on your books
A refund appears on your settlement file as a `refund` row with a negative `net_amount`. Two important details for accounting:
<details>
<summary>The processing fee on the original payment is not returned</summary>
When you take a payment, Evolve charges the processing fee. When you refund it, the fee stays — you've already paid the network. The settlement file records this as the `fee` column being `0.00` on the refund row, but the original `fee` on the payment row remains. Net result: a refunded payment costs you the original processing fee in lost revenue.
</details>
<details>
<summary>Partial refunds are separate line items</summary>
Three partial refunds against one payment produce three `refund` rows on the settlement file, each with its own ID. The `source_id` column ties them all back to the original payment.
</details>
## Bulk refunds
For situations where you need to refund many payments at once — a recalled product, a service outage credit — you can issue refunds in bulk:
* **From the dashboard:** Filter to the payments you want to refund, click **Bulk action → Refund**, confirm.
* **From a CSV:** Upload a list of payment IDs and amounts under **Payments → Bulk refunds**.
Bulk refunds run in the background and you'll get an email when they complete. If any individual refund fails (typically because the payment is too old or already fully refunded), it's flagged on the result file and the rest still go through.
## Customer-initiated refund requests
Some teams expose a "Get a refund" flow to customers directly — most often through a customer portal or a help center. You can wire one up in two ways:
1. **Email-based** — a `mailto:` link routes the request to your support inbox; an agent issues the refund from the dashboard.
2. **Self-serve** — the customer requests a refund from your portal, your code calls the API, and the refund issues automatically.
Self-serve refunds are great for low-value, low-risk products. For higher-value items, route refunds through a human approval step — Evolve has no anti-fraud rules on refunds since they're considered low-risk by default.
## Related
* [Settlement files](settlement-files.md) — how refunds appear in your daily reconciliation.
* [Disputes and chargebacks](disputes.md) — what to do when a refund isn't enough and the customer disputes.
references/example-site/products/payments/reconciliation/settlement-files.md
---
icon: file-csv
description: The daily CSV that explains every cent moving in and out of your account.
---
# Settlement files
Every business day at <code class="expression">space.vars.settlement_time_utc</code>, Evolve produces a CSV that lists every payment, refund, fee, dispute deduction, and adjustment that affected your balance during the previous day. The file is the source of truth — what's on it equals exactly what shows up in your bank account on payout day.
## Where to find it
Three ways, in order of decreasing effort:
<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Dashboard</strong></td><td>Manual download. Best for occasional use.</td><td></td></tr><tr><td><strong>Email</strong></td><td>Daily attachment to your finance distribution list.</td><td></td></tr><tr><td><strong>Scheduled export</strong></td><td>Automatic push to SFTP, S3, or GCS.</td><td><a href="../reporting/sharing-exports.md">sharing-exports.md</a></td></tr></tbody></table>
## File structure
A single file per settlement day. The filename contains the settlement date and your account ID:
```
evolve-settlement-2026-04-29-acct_8L2pK9q.csv
```
Each row is one line item. Multiple rows can belong to the same payment — for example, a captured payment and the fee for it appear on separate rows.
## Columns
| Column | Description | Example |
| --- | --- | --- |
| `settlement_date` | The settlement date this row belongs to | `2026-04-29` |
| `id` | Unique line item ID | `stl_li_3KsM12pL9qXa7` |
| `type` | The line type — see below | `payment` |
| `source_id` | The originating object | `pay_3KsM12pL9qXa7` |
| `description` | Free-text description from the original payment | `Order #1042` |
| `gross_amount` | Amount before fees | `42.00` |
| `fee` | Evolve fee for this line | `-1.52` |
| `net_amount` | What hit your balance | `40.48` |
| `currency` | ISO 4217 code | `USD` |
| `customer_id` | If applicable | `cus_4n2P3qR5sT6uV` |
| `metadata.*` | Custom metadata you attached at payment creation | varies |
The full column reference, including all `type` values, lives at the top of every file as a comment row — no need to memorize anything.
## Line types
| Type | When you'll see it |
| --- | --- |
| `payment` | A captured payment on the settlement day |
| `refund` | A refund issued on the settlement day |
| `dispute_lost` | The disputed amount + dispute fee, deducted |
| `dispute_won` | The disputed amount returned, fee not refunded |
| `payout` | The net amount sent to your bank (one row per file) |
| `adjustment` | A manual adjustment made by Evolve (rare) |
| `reserve_release` | A previously held amount released to your balance |
## A small example
A day with one payment, one refund, and a payout looks like this:
```csv
settlement_date,id,type,source_id,gross_amount,fee,net_amount,currency
2026-04-29,stl_li_a1,payment,pay_3KsM12pL9qXa7,42.00,-1.52,40.48,USD
2026-04-29,stl_li_a2,payment,pay_5n8R4qT2bX9c1,128.00,-3.78,124.22,USD
2026-04-29,stl_li_a3,refund,re_2pK9qL3sM4tN5,-42.00,0.00,-42.00,USD
2026-04-29,stl_li_a4,payout,po_7vY3wZ8aB2cD9,0.00,0.00,-122.70,USD
```
The payout row's `net_amount` is the negative of the sum of the other rows — it's what leaves your Evolve balance and lands in your bank.
## Reconciling against your bank
A simple daily process:
{% stepper %}
{% step %}
### Match the payout to the bank deposit
The `payout` row's `net_amount` (as a positive number) should equal exactly one credit on your bank statement, dated per your plan's payout schedule.
{% endstep %}
{% step %}
### Book each payment as revenue
Sum the `payment` rows' `gross_amount` — that's your revenue for the day. The corresponding `fee` rows are processing-fee expenses.
{% endstep %}
{% step %}
### Book refunds and disputes
`refund` rows reduce revenue. `dispute_lost` rows reduce revenue and add a dispute-fee expense.
{% endstep %}
{% step %}
### Verify the file balances
Sum the `net_amount` column. It should equal zero — every cent that came in either left in the payout, was netted by a refund/dispute, or moved into reserves.
{% endstep %}
{% endstepper %}
If the file doesn't balance to zero, something is off — open a support ticket with the file attached and we'll trace it.
## Going further
* Push files automatically to your data warehouse — see [Sharing and scheduled exports](../reporting/sharing-exports.md).
* Build reconciliation into QuickBooks or NetSuite — see [Guides / Integrations](https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/).
* For deeper analysis, the same data is available as queryable [Reports](../reporting/standard-reports.md).
references/example-site/products/payments/reporting/custom-reports.md
---
icon: sliders
description: Build your own views on top of Evolve's data — without writing SQL.
---
# Custom reports
Custom reports let you build views beyond the [standard reports](standard-reports.md) — different breakdowns, different filters, different chart types — and save them for your team. They run against the same underlying data, so they're always consistent with what shows up in finance reports.
{% if visitor.claims.unsigned.plan === "starter" %}
{% hint style="warning" icon="lock" %}
**Custom reports are a Growth and Enterprise feature.** Starter accounts get full access to standard reports and can save filtered views, but can't build new chart types from scratch.
{% endhint %}
{% endif %}
## What you can build
Three kinds of report, each backed by a different builder:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-chart-line" style="color:$primary;">:chart-line:</i></h3></td><td><strong>Time series</strong></td><td>A metric over time, optionally split by a dimension.</td><td></td></tr><tr><td><h3><i class="fa-users-line" style="color:$primary;">:users-line:</i></h3></td><td><strong>Cohort</strong></td><td>Customer groups followed forward in time.</td><td></td></tr><tr><td><h3><i class="fa-table-cells" style="color:$primary;">:table-cells:</i></h3></td><td><strong>Pivot</strong></td><td>Two-dimensional table, one metric per cell.</td><td></td></tr></tbody></table>
## Building a time-series report
The most common kind. An example: "monthly net revenue, split by payment method, last 12 months."
{% stepper %}
{% step %}
### Pick a metric
In **Reports → New report**, choose **Time series** and pick a metric — net revenue, payment count, refund rate, dispute count, approval rate, fee total.
{% endstep %}
{% step %}
### Pick a time grain
Day, week, or month. The grain locks the start of each bucket — daily uses your account timezone, weekly buckets start on Monday, monthly buckets on the 1st.
{% endstep %}
{% step %}
### Add a split (optional)
Split the metric by a dimension — payment method, currency, region, customer cohort, acquirer, or a metadata field you've attached at payment creation. The chart turns into stacked bars or a multi-line chart.
{% endstep %}
{% step %}
### Add filters
Restrict the dataset before charting — for example, "only payments above $100" or "only customers tagged `enterprise`."
{% endstep %}
{% step %}
### Save and share
Click **Save**. Name it, give it an emoji icon, optionally pin it to your account dashboard. From here you can also schedule it (see [Sharing and scheduled exports](sharing-exports.md)).
{% endstep %}
{% endstepper %}
## Building a cohort report
Cohort reports answer questions like "what fraction of customers acquired in March were still paying us in June?" — useful for subscription businesses and any product where return purchases matter.
* **Cohort definition:** the event that puts a customer into a cohort (first payment, first subscription, sign-up).
* **Cohort grain:** weekly or monthly.
* **Forward metric:** what you're measuring over time — retention, cumulative revenue, refund rate.
The result is the classic triangular cohort chart, with each cohort as a row and time-since-cohort as columns.
## Building a pivot report
Pivot reports give you a two-dimensional grid — one dimension per axis, one metric per cell. An example: "decline rate by issuer country and card brand, last 30 days."
The builder is similar to a spreadsheet pivot table:
* **Rows** — one dimension.
* **Columns** — another dimension.
* **Cell value** — the metric, e.g. count, sum, average, percentile.
Pivot reports are great for spotting outliers — a single bad row often jumps out.
## Available metrics
| Metric | Description |
| --- | --- |
| `payment_count` | Number of payments |
| `payment_volume_gross` | Sum of payment amounts before fees |
| `payment_volume_net` | After fees and refunds |
| `approval_rate` | Approved / (approved + declined) |
| `decline_rate` | Declined / total |
| `refund_count` | Number of refunds |
| `refund_volume` | Sum of refund amounts |
| `dispute_count` | Number of disputes opened |
| `dispute_rate` | Disputes / payments |
| `dispute_win_rate` | Won / (won + lost) |
| `fee_total` | Total processing fees paid |
| `customer_count` | Distinct customers in the period |
## Available dimensions
| Dimension | Examples |
| --- | --- |
| `payment_method` | `card`, `ach_debit`, `wire`, `sepa`, ... |
| `card_brand` | `visa`, `mastercard`, `amex`, ... |
| `currency` | `usd`, `eur`, `gbp`, ... |
| `region` | `us`, `eu`, `apac`, ... |
| `acquirer` | The processor used |
| `customer_cohort` | A cohort tag you've defined |
| `metadata.<key>` | Any metadata field on the underlying payment |
## Saving and sharing
Custom reports live in your workspace and respect your team's permissions. You can:
* **Pin** a report to your account dashboard so it loads on first sign-in.
* **Share** a read-only link with anyone in your workspace.
* **Schedule** it to email or push to a destination — see [Sharing and scheduled exports](sharing-exports.md).
* **Export** the underlying data to CSV.
## Limits
* Up to **50 saved custom reports** per workspace included; **$50/month per additional 50** beyond that.
* Reports run over the last **24 months** of data by default. For older data, see [Exporting to BI tools](exporting.md).
* Reports refresh on demand. A scheduled refresh runs as part of the schedule itself.
## Related
* [Standard reports](standard-reports.md) — the pre-built starting points.
* [Exporting to BI tools](exporting.md) — for anything that doesn't fit the report builder.
references/example-site/products/payments/reporting/exporting.md
---
icon: file-export
description: Pipe Evolve data into your data warehouse, BI tool, or accounting system.
---
# Exporting to BI tools
For most businesses, the dashboard is enough — you build reports, save the views you care about, and check them daily. But if your data team is the source of truth for finance reporting, or you want to combine Evolve data with marketing, product, and ops data in one place, you'll want it pushed into your warehouse.
## What's available
Three categories of pre-built integration:
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-warehouse" style="color:$primary;">:warehouse:</i></h3></td><td><strong>Data warehouses</strong></td><td>Snowflake, BigQuery, Redshift, Databricks.</td><td></td></tr><tr><td><h3><i class="fa-chart-pie" style="color:$primary;">:chart-pie:</i></h3></td><td><strong>BI tools</strong></td><td>Looker, Tableau, Mode, Metabase.</td><td></td></tr><tr><td><h3><i class="fa-calculator" style="color:$primary;">:calculator:</i></h3></td><td><strong>Accounting</strong></td><td>QuickBooks, NetSuite, Xero.</td><td><a href="https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/">README.md</a></td></tr></tbody></table>
If your tool isn't listed, you can also:
* Pull data via the [reporting API](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api/reports) (requires engineering effort).
* Use [scheduled CSV exports](sharing-exports.md) to SFTP or S3 and pick them up from there.
## Data warehouses
Evolve's warehouse connectors push a defined schema of tables on a schedule — typically every hour. The tables include:
| Table | Contents |
| --- | --- |
| `payments` | One row per payment, current state |
| `payment_events` | One row per state change, full history |
| `refunds` | One row per refund |
| `disputes` | One row per dispute, including evidence and outcome |
| `customers` | One row per customer |
| `payment_methods` | Saved methods linked to customers |
| `settlements` | Daily settlement summaries |
| `settlement_line_items` | One row per settlement line — equivalent to the [CSV file](../reconciliation/settlement-files.md) |
| `acquirer_routes` | Per-payment routing decisions and outcomes |
Joining these is straightforward — every table has a `payment_id` or `customer_id` column where applicable.
### Setting up a warehouse connector
{% stepper %}
{% step %}
### Pick the warehouse
In **Settings → Data → Warehouse connectors**, click the warehouse you want and follow the auth flow. We support OAuth where the warehouse offers it, otherwise it's a service-account credential.
{% endstep %}
{% step %}
### Pick the dataset
Choose which database and schema Evolve should write to. We strongly recommend a dedicated schema — `evolve_raw` is a common choice — so the connector can manage its own tables without touching anything else.
{% endstep %}
{% step %}
### Pick the schedule
Hourly is the default. You can go to every 15 minutes on Enterprise. Daily is fine if your reporting cadence is daily.
{% endstep %}
{% step %}
### Trigger the first sync
Click **Sync now** to backfill. The first sync pulls the last 24 months of data and can take a few hours for large accounts; we'll email you when it's done.
{% endstep %}
{% endstepper %}
## BI tool integrations
The BI integrations are mostly thin wrappers around the warehouse connectors — they're a convenience for teams that want a curated set of reports out of the box.
When you connect Looker, Tableau, Mode, or Metabase, you get:
* A **starter dashboard** mirroring our [standard reports](standard-reports.md).
* A **semantic model / explore** so non-technical users can build queries by dragging fields.
* **Drill-through links** from charts back to the Evolve dashboard for the underlying payment.
You're not locked in — once data is in the warehouse, you can build whatever you want.
## Accounting integrations
QuickBooks, NetSuite, and Xero integrations work differently — they push **journal entries**, not raw data. Each settlement file produces one journal entry per accounting period, with the right account codes for revenue, fees, refunds, and disputes.
See [Guides / Integrations](https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/) for the per-tool setup.
## Related
* [Sharing and scheduled exports](sharing-exports.md) — CSV-based pushes, and email distributions.
* [Reporting API](https://app.gitbook.com/s/Si95BtOt1VRLWjT7A67V/payments-api/reports) — pull data programmatically.
* [Settlement files](../reconciliation/settlement-files.md) — the daily CSV that finance imports.
references/example-site/products/payments/reporting/README.md
---
description: See what your business is doing — by day, by product, by customer, by acquirer.
icon: chart-line
---
# Reporting
Everything that happens in Evolve is queryable. Reports turn raw events into the views your team actually looks at — daily revenue, top customers, decline reasons, dispute rates, the lift from smart routing. They're available in the dashboard, exportable to CSV, and pushable to your data warehouse.
{% if visitor.claims.unsigned.persona === "partner" %}
{% hint style="info" icon="building" %}
**Setting up reporting at the enterprise level?** You'll likely want **scheduled exports** to your warehouse so your BI tool is the source of truth, not the dashboard. Skip ahead to [Sharing and scheduled exports](sharing-exports.md).
{% endhint %}
{% endif %}
{% if visitor.claims.unsigned.persona === "new" %}
{% hint style="info" icon="hand-wave" %}
**New to Evolve?** The fastest way to get a feel for what's possible is to open [Standard reports](standard-reports.md) — they're already populated with your test-mode activity.
{% endhint %}
{% endif %}
## What reports give you
<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><i class="fa-chart-bar" style="color:$primary;">:chart-bar:</i></h3></td><td><strong>Standard reports</strong></td><td>Pre-built views finance and ops use every day.</td><td><a href="standard-reports.md">standard-reports.md</a></td></tr><tr><td><h3><i class="fa-sliders" style="color:$primary;">:sliders:</i></h3></td><td><strong>Custom reports</strong></td><td>Build your own views on top of the same data.</td><td><a href="custom-reports.md">custom-reports.md</a></td></tr><tr><td><h3><i class="fa-file-export" style="color:$primary;">:file-export:</i></h3></td><td><strong>Exporting to BI tools</strong></td><td>Push to Looker, Tableau, your warehouse.</td><td><a href="exporting.md">exporting.md</a></td></tr><tr><td><h3><i class="fa-share-nodes" style="color:$primary;">:share-nodes:</i></h3></td><td><strong>Sharing and scheduled exports</strong></td><td>Email distributions, SFTP and S3 pushes.</td><td><a href="sharing-exports.md">sharing-exports.md</a></td></tr></tbody></table>
## What you can answer
A few examples of what falls out of the standard reports:
| Question | Report |
| --- | --- |
| What did we make yesterday, after fees? | Daily revenue |
| Which payment methods are growing? | Methods over time |
| What's our approval rate, and is smart routing helping? | Routing report |
| Which customers are top of the LTV chart? | Top customers |
| What's our dispute rate this quarter? | Dispute report |
| Why did this batch of payments fail? | Decline reasons |
| Did we collect enough to cover the upcoming payouts? | Cash position |
## How reports stay fresh
* **Dashboard charts** update in near-real-time — typically a few seconds behind the underlying event.
* **Daily reports** (revenue, methods, declines) cut over at midnight in your account's timezone, set in **Settings → Account → Timezone**.
* **Settlement-anchored reports** (anything tied to a specific payout) close at the daily settlement cut-off, <code class="expression">space.vars.settlement_time_utc</code>.
* **Custom reports** run on demand; saved views can be scheduled to refresh and email/post to a destination.
## Permissions
Reports respect your role's data scope:
| Role | Can see |
| --- | --- |
| Viewer | Aggregate reports, no per-customer data |
| Finance | Everything in this section, plus settlement files |
| Ops | Aggregate reports + per-customer history needed to support |
| Admin | Everything, plus permission to schedule and share |
Configure in **Settings → Team → Roles**.
## Related
* [Settlement files](../reconciliation/settlement-files.md) — the source data for all finance reports.
* [Smart routing](../accept-payments/smart-routing.md) — what the Routing report measures.
* [Guides / Integrations](https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/) — pre-built connectors to QuickBooks, NetSuite, Looker, and more.
references/example-site/products/payments/reporting/sharing-exports.md
---
icon: share-nodes
description: Push reports and settlement files to email, Slack, SFTP, or cloud storage on a schedule.
---
# Sharing and scheduled exports
Once you've built a report you check daily, the next step is to stop opening the dashboard to look at it. Evolve can push any standard or custom report — and any settlement file — to wherever your team already works.
## Destinations
| Destination | Format | Best for |
| --- | --- | --- |
| **Email** | CSV attachment + summary in body | Daily/weekly digest to a finance distribution list |
| **Slack** | Summary message + CSV attachment | Ops channel notifications |
| **SFTP** | CSV upload | Legacy finance systems and managed-service providers |
| **AWS S3** | CSV upload | Most data-engineering setups |
| **Google Cloud Storage** | CSV upload | GCP-first organizations |
| **Webhook** | JSON POST with summary + signed download URL | Custom downstream automation |
## Setting up a scheduled export
{% stepper %}
{% step %}
### Pick the report
From any standard or custom report, click **Schedule**. Or go to **Reports → Schedules → New** and pick a report from the list.
{% endstep %}
{% step %}
### Pick the cadence
Daily, weekly (pick a day), monthly (pick a date), or after each settlement (recommended for finance flows).
{% endstep %}
{% step %}
### Pick the destination
Choose one of the destinations above and authenticate. For SFTP, S3, and GCS, Evolve gives you a service account or set of static IPs to allow-list.
{% endstep %}
{% step %}
### Pick the format
CSV is the default. For some reports, JSON is also available. Choose whether to include a header row and which timezone date columns should use.
{% endstep %}
{% step %}
### Save
The first export runs on the next scheduled tick. You can also trigger an immediate test from the same screen.
{% endstep %}
{% endstepper %}
## Daily settlement push
The most common scheduled export setup:
* **Source:** the settlement file (one CSV per day).
* **Cadence:** every day after the <code class="expression">space.vars.settlement_time_utc</code> cut-off.
* **Destination:** SFTP or S3 in your finance environment.
* **Format:** CSV, UTF-8, with a header row.
This puts the file your accounting team imports each morning into a known location, named by date — `evolve-settlement-2026-04-29.csv` — and your existing import script can pick it up without any change to the dashboard workflow.
## Email distributions
Email is the right destination when:
* The recipients are humans, not systems.
* The cadence is weekly or monthly (daily emails get ignored).
* The data is summary-level, not row-by-row reconciliation.
You can configure up to 10 recipient addresses per scheduled email. Each email contains:
* A short summary in the body — "Daily revenue: $42,180. Up 5% from last week."
* A CSV attachment with the full data.
* A direct link back to the live dashboard view.
Emails are sent from `reports@evolve.com`. Make sure the recipients' mail servers don't filter it.
## Slack
The Slack integration posts a summary message to a channel you've connected, with a CSV attachment for the underlying data. The message format is configurable — you can include the headline metric, a chart preview (as an image), and a link back to the dashboard.
To connect Slack, install the [Evolve Slack app](https://app.gitbook.com/s/MBT3EDUK7DzXmR0k9cje/slack). The same app can also post real-time alerts for things like dispute openings and large refunds.
## Webhooks for finance automation
For more advanced automation — building a custom finance workflow, triggering a downstream pipeline, posting to a tool not in our destination list — use the webhook destination. Each scheduled run POSTs a JSON payload to your endpoint:
```json
{
"schedule_id": "sched_3K2pL9q",
"report_id": "rpt_8M2nF6m",
"ran_at": "2026-04-30T06:01:14Z",
"row_count": 412,
"summary": { "net_revenue": 42180.55, "currency": "USD" },
"download_url": "https://api.evolve.com/v1/reports/runs/rpt_8M2nF6m/download?token=...",
"download_expires_at": "2026-05-01T06:01:14Z"
}
```
The download URL is signed and valid for 24 hours. Your code fetches the CSV when it's ready to process — no need to receive megabytes of data in the webhook itself.
## Auditing scheduled exports
Every scheduled run is logged in **Reports → Schedules → History**, with:
* When it ran.
* Whether it succeeded.
* Where it was delivered.
* The output file (downloadable for 30 days).
If a destination fails (SFTP timeout, S3 permission denied), you'll get an email and the failed run is retried up to 3 times over an hour.
## Permissions and security
Scheduled exports respect the role permissions of the user who created them. If that user's role changes — or they leave the team — the schedule pauses until an admin reassigns it.
For sensitive destinations, you can require a second admin to approve the schedule before it goes live. Toggle this in **Settings → Security → Approval policies**.
## Related
* [Standard reports](standard-reports.md) — what to schedule first.
* [Custom reports](custom-reports.md) — building reports worth scheduling.
* [Exporting to BI tools](exporting.md) — for warehouse-level integration.
* [Settlement files](../reconciliation/settlement-files.md) — the most-scheduled file.
references/example-site/products/payments/reporting/standard-reports.md
---
icon: chart-bar
description: The pre-built reports finance and ops teams use every day.
---
# Standard reports
Standard reports cover the questions every payments team needs to answer regularly. They're available to every account, on every plan, and they're the right starting point before deciding whether you need a custom report.
Each report has the same structure:
* A **default view** that loads when you open it.
* **Filters** (date range, currency, payment method, customer cohort) at the top.
* An **export to CSV** button.
* A **save as custom report** button to lock in your filter set.
## The reports
### Daily revenue
How much you made each day, after processing fees and refunds. The default view shows the last 30 days.
* **Numerator:** sum of `payment.net_amount` − sum of `refund.net_amount` (both in your default currency).
* **Filters:** payment method, currency, customer cohort, region.
* **Common usage:** finance team's daily check; CFO's "are we trending?" question.
### Methods over time
The mix of payment methods used, by day or month, as a stacked area chart.
* **Useful for:** spotting growth in ACH or international rails; deciding whether to invest in a new method.
* **Filters:** date range, region.
### Routing report
Per-acquirer approval rate, in normal and degraded modes, plus the lift from [smart routing](../accept-payments/smart-routing.md) and any [failover](../accept-payments/failover.md) events.
* **Default view:** approval rate per acquirer, last 7 days.
* **Filters:** card brand, currency, BIN country.
* **Why it matters:** the headline number is "lift vs. baseline" — how many additional payments smart routing approved that wouldn't have approved on a single-acquirer setup.
### Decline reasons
Why declines happened, grouped by `decline_code`, with a sortable count column.
* **Default view:** last 30 days, sorted by count descending.
* **Useful for:** spotting `expired_card` clusters (set up account updater), `incorrect_zip` problems (review your AVS form), or sudden `do_not_honor` spikes (often an issuer-side incident).
### Top customers
Customers ranked by lifetime value, with a sortable column for refund rate, dispute rate, and last payment date.
* **Useful for:** support prioritization, churn analysis, and identifying the small slice of customers driving disputes.
* **Privacy note:** this report respects your retention policy — customers whose data has been auto-purged don't appear.
### Dispute report
Disputes opened, won, lost, and pending — per month, per reason code.
* **Default view:** last 6 months, all reason codes.
* **Useful for:** watching the dispute rate vs. the network's 1.0% threshold, and spotting reason-code patterns that indicate a systemic product or fulfillment issue.
* **Tied to:** [Disputes and chargebacks](../reconciliation/disputes.md).
### Cash position
Money in your Evolve balance now, money in your bank account from past payouts, money on the way (pending captures, in-flight payouts, held disputes).
* **Useful for:** "do we have enough to cover the upcoming payroll run?" type questions.
* **Filters:** currency.
* **Live updates:** balances refresh every 15 seconds.
### Reconciliation summary
A daily breakdown of payments, fees, refunds, and net to bank — designed to be the basis of your accounting close.
* **Output:** matches the structure of the [settlement file](../reconciliation/settlement-files.md), aggregated by day.
* **Useful for:** the daily reconciliation routine; finance team's first report of the day.
## Saving a customized view
If you load a standard report, change the filters or the date range to something you'll want again, click **Save as custom report**. It moves into your **Custom reports** tab and shows up at the top of the dashboard's report list.
See [Custom reports](custom-reports.md) for the full custom-report capabilities.
## Scheduling a report
Any standard or saved custom report can be scheduled to:
* Email a CSV to a list of recipients.
* Push a CSV to SFTP, S3, or GCS.
* Post a summary to Slack.
See [Sharing and scheduled exports](sharing-exports.md) for the configuration.
## Related
* [Custom reports](custom-reports.md) — building your own views.
* [Exporting to BI tools](exporting.md) — pushing the underlying data to your warehouse.
* [Settlement files](../reconciliation/settlement-files.md) — the source data for finance reports.
references/example-site/products/payments/SUMMARY.md
# Table of contents
* [Payments](README.md)
## Quickstart
* [Accept your first payment](quickstart/accept-your-first-payment.md)
* [Test mode and live mode](quickstart/test-and-live-mode.md)
## Concepts
* [Payment lifecycle](concepts/payment-lifecycle.md)
* [Payment methods](concepts/payment-methods.md)
* [Money movement and settlement](concepts/money-movement.md)
* [Fees and pricing](concepts/fees-and-pricing.md)
## Accept payments
* [Overview](accept-payments/README.md)
* [Take a payment](accept-payments/take-a-payment.md)
* [Saved payment methods](accept-payments/saved-payment-methods.md)
* [3-D Secure and SCA](accept-payments/3d-secure.md)
* [Smart routing](accept-payments/smart-routing.md)
* [Failover and retries](accept-payments/failover.md)
## Reconciliation
* [Overview](reconciliation/README.md)
* [Settlement files](reconciliation/settlement-files.md)
* [Refunds](reconciliation/refunds.md)
* [Disputes and chargebacks](reconciliation/disputes.md)
## Reporting
* [Overview](reporting/README.md)
* [Standard reports](reporting/standard-reports.md)
* [Custom reports](reporting/custom-reports.md)
* [Exporting to BI tools](reporting/exporting.md)
* [Sharing and scheduled exports](reporting/sharing-exports.md)
references/example-site/PRUNE-NOTES.md
# PRUNE-NOTES — what's missing from this bundled copy
This `example-site/` is a curated subset of the full Evolve Demo repo, trimmed to keep the skill under client file limits while preserving every distinctive structural and syntactic pattern. The original site has more content; if you want to study it in its complete form, look at the source repo on GitHub or the published site.
The full original tree is faithfully captured in two files alongside this one — read them when you need the un-pruned picture:
- **`structure.json`** — the response from `GET /v1/orgs/{orgId}/sites/{siteId}/structure` for the original site. Lists every section, section-group, and site-space (including all auto-translated language variants). The English site-spaces it lists are what the original Git repo had as folders; everything pruned below is still represented here.
- **`SUMMARY.md`** files within each remaining space — these still describe the original page tree, including any pages whose individual `.md` files were removed in the prune. Treat the SUMMARY as canonical for the original IA.
## What was removed
### `developers/v1/` and `developers/v3/` — entire folders gone
The original site demonstrated a **versioned developer documentation** pattern: three coexisting versions of the developer docs side-by-side, used to show how a real product handles API lifecycles in GitBook.
- `developers/v1/` — the **legacy** version (older API, kept around for customers who haven't migrated).
- `developers/v2/` — the **current stable** version (kept in this bundled copy).
- `developers/v3/` — the **beta / prerelease** version (next major API, available for early adopters).
All three folders had the same internal structure:
```
developers/v{N}/
├── README.md
├── SUMMARY.md
├── .gitbook/vars.yaml
├── connect-api/README.md
├── identity-api/README.md
├── payments-api/README.md
├── getting-started/
│ ├── authentication.md
│ ├── conventions.md
│ ├── for-ai-agents.md
│ ├── quickstart.md
│ └── sdks.md
├── mcp/
│ ├── README.md
│ └── connecting-an-agent.md
└── webhooks/
├── README.md
├── event-catalog.md
├── retries-and-replay.md
└── verifying-signatures.md
```
The content differences across versions were small and version-specific — endpoint paths, deprecation notices, breaking-change callouts, slightly different OpenAPI references. The structural and component patterns are identical, so v2 alone is a faithful representative.
The `developers/openapi/` folder still contains all three versioned spec files (`v1/`, `v2/`, `v3/`) — those are tiny and demonstrate the pattern of co-located versioned OpenAPI specs that the developer spaces reference.
**The pattern to take away**: when a product has multiple coexisting API versions, give each its own space, group them under one section ("Developers"), and keep their structure parallel so users can compare like-for-like across versions. Each version's space gets its own folder in the Git repo. The customization can use a top-level navigation hint (e.g. "v2 is current — view [v1](legacy) or [v3 beta](preview)") to orient visitors.
### `connections/blog/`, `community/`, `youtube/` — kept one article each
The original site had **6–7 article HTML files per connections subfolder**, intended as external content surfaces that the GitBook AI assistant indexes alongside the docs. These aren't pages in any space — they're standalone HTML files that simulate "the company's blog / forum / YouTube channel" for demo purposes.
Each subfolder now contains:
- The `index.html` (which lists what was there originally — keep this as a record of what existed)
- One representative article showing the metadata pattern
What this demonstrates:
- **Blog**: `<meta name="author">` (real-name byline), `article:published_time`, topical `keywords`. Standard editorial content. Sample kept: `same-day-payouts-tradeoffs.html`.
- **Community forum**: similar shape but `author` is a username/handle — gives the AI assistant a different signal about authority and tone. Sample kept: `webhook-retries-backoff.html`.
- **YouTube**: distinctive `<meta property="video:duration">` and `og:type=video.other` — useful when the AI assistant wants to recommend video content over written content. Sample kept: `webhooks-deep-dive.html`.
The `index.html` files in each subfolder reference the full original article list, so the original shape is recoverable from those.
**The pattern to take away**: external content surfaces (blog, forum, video) that the AI assistant pulls from alongside docs should each have consistent metadata (`author`, `published_time`, `keywords` at minimum) so the assistant can attribute and rank them. Different content types benefit from type-specific OpenGraph metadata (e.g. video duration).
## What was kept and why
Everything else is intact, including:
- All product spaces (`payments`, `identity`, `connect`) with their full page trees
- The `home/` space with its `.gitbook/includes/` content blocks
- All `guides/` subspaces (help-center, integrations, tutorials)
- `partners/` and `changelog/` in full
- `developers/v2/` (the canonical example) and `developers/openapi/` (all three versions)
- All `.gitbook/vars.yaml` files
- All `SUMMARY.md` files
- Top-level `customization.json` and `structure.json`
references/example-site/README.md
# Evolve Demo
Demo content for the Evolve docs site — a fictional payments platform used to showcase GitBook features in customer demos.
Published at [gitbook.com/evolve-demo](https://gitbook.com/evolve-demo) via Git Sync. Edits to this repo flow to the published site; edits in GitBook flow back here.
references/example-site/structure.json
{
"type": "sections",
"structure": [
{
"object": "site-section",
"id": "sitesc_vcUkL",
"icon": "house",
"title": "Home",
"localizedTitle": {
"de": "Startseite",
"en": "Home",
"es": "Inicio",
"fr": "Accueil",
"zh": "首页"
},
"localizedDescription": {},
"path": "home",
"default": true,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_qHYOj",
"path": "docs",
"default": true,
"section": "sitesc_vcUkL",
"space": {
"object": "space",
"id": "V2euUoapjerbu1hCxVIv",
"title": "Home",
"emoji": "1f3e0",
"visibility": "public",
"createdAt": "2026-04-30T09:25:09.782Z",
"updatedAt": "2026-05-01T16:47:40.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/V2euUoapjerbu1hCxVIv",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/V2euUoapjerbu1hCxVIv/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:25:55.467Z"
},
"revision": "0NCaFSGMPHClGTA0QxTj",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 24,
"changeRequestsDraft": 1,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_686Tq",
"path": "fr",
"default": false,
"section": "sitesc_vcUkL",
"space": {
"object": "space",
"id": "95FEGDXMdN2DeINEBB4j",
"title": "Home (FR)",
"emoji": "2712",
"visibility": "public",
"createdAt": "2026-05-04T09:07:50.283Z",
"updatedAt": "2026-05-04T09:07:50.283Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/95FEGDXMdN2DeINEBB4j",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/95FEGDXMdN2DeINEBB4j/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/home/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "9t4ui3NNBWknKjbJllf2",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_HdHGD",
"path": "de",
"default": false,
"section": "sitesc_vcUkL",
"space": {
"object": "space",
"id": "o7IBh9p0eHlv8JY6Hvpk",
"title": "Home (DE)",
"emoji": "1f4c4",
"visibility": "public",
"createdAt": "2026-05-04T09:12:39.755Z",
"updatedAt": "2026-05-04T09:12:39.755Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/o7IBh9p0eHlv8JY6Hvpk",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/o7IBh9p0eHlv8JY6Hvpk/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/home/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "jmJs1iYCczVTmELRDAQB",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ClCas",
"path": "es",
"default": false,
"section": "sitesc_vcUkL",
"space": {
"object": "space",
"id": "x05WkANgvIGWzpPeG2bz",
"title": "Home (ES)",
"emoji": "1f4d5",
"visibility": "public",
"createdAt": "2026-05-04T09:09:42.371Z",
"updatedAt": "2026-05-04T09:09:42.371Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/x05WkANgvIGWzpPeG2bz",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/x05WkANgvIGWzpPeG2bz/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/home/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "R4MdDUS2juaE8PZpve4k",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_8A8dt",
"path": "zh",
"default": false,
"section": "sitesc_vcUkL",
"space": {
"object": "space",
"id": "OHxbVNVGQ6gRb1HOUfBt",
"title": "Home (ZH)",
"emoji": "1f4ca",
"visibility": "public",
"createdAt": "2026-05-04T09:10:02.198Z",
"updatedAt": "2026-05-04T09:10:02.198Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/OHxbVNVGQ6gRb1HOUfBt",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/OHxbVNVGQ6gRb1HOUfBt/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/home/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "XURpVGksLDOdKBypJ6GO",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/home/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/"
}
},
{
"object": "site-section-group",
"id": "sitescg_USLlo",
"icon": "angles-up",
"title": "Products",
"localizedTitle": {
"de": "Produkte",
"en": "Products",
"es": "Productos",
"fr": "Produits",
"zh": "产品"
},
"draft": false,
"sections": [
{
"object": "site-section",
"id": "sitesc_oCmWt",
"sectionGroup": "sitescg_USLlo",
"icon": "credit-card",
"title": "Payments",
"localizedTitle": {
"de": "Zahlungen",
"en": "Payments",
"es": "Pagos",
"fr": "Paiements",
"zh": "支付"
},
"description": "Route, reconcile, and report",
"localizedDescription": {
"de": "Weiterleiten, abstimmen und berichten",
"en": "Route, reconcile, and report",
"es": "Enrutar, conciliar e informar",
"fr": "Acheminer, réconcilier et rapporter",
"zh": "路由、对账和报告"
},
"path": "payments",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_A7GFO",
"path": "en",
"default": true,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "w3LlITSOQye8o4wjsQXV",
"title": "Payments",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-04-30T10:47:06.632Z",
"updatedAt": "2026-05-01T16:47:42.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/w3LlITSOQye8o4wjsQXV",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/w3LlITSOQye8o4wjsQXV/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:27:16.422Z"
},
"revision": "DfklbYYK8im7X8mSQb7U",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 3,
"changeRequestsDraft": 1,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_y3yQ6",
"path": "fr",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "3h1wtBbGcXTLyEoV3qeq",
"title": "Payments (FR)",
"emoji": "1f58b",
"visibility": "public",
"createdAt": "2026-05-04T09:10:27.458Z",
"updatedAt": "2026-05-04T09:10:27.458Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/3h1wtBbGcXTLyEoV3qeq",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/3h1wtBbGcXTLyEoV3qeq/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "baq8cXcmxlaXyGQduh2y",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_rhrla",
"path": "de",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "W4iKHxx7gM4wKWvJzP2f",
"title": "Payments (DE)",
"emoji": "1f4da",
"visibility": "public",
"createdAt": "2026-05-04T09:12:53.503Z",
"updatedAt": "2026-05-04T09:12:53.503Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/W4iKHxx7gM4wKWvJzP2f",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/W4iKHxx7gM4wKWvJzP2f/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "LZyeWISTxI1vkLChjTxz",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ZBZ0j",
"path": "es",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "NXIELvRCE9zYhxFzsOV8",
"title": "Payments (ES)",
"emoji": "270d",
"visibility": "public",
"createdAt": "2026-05-04T09:10:40.746Z",
"updatedAt": "2026-05-04T09:10:40.746Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/NXIELvRCE9zYhxFzsOV8",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/NXIELvRCE9zYhxFzsOV8/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "RY1AzV746PoFWM0j7RfP",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_QcQFf",
"path": "zh",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "ioRirBSFjpJD6vqlbyyy",
"title": "Payments (ZH)",
"emoji": "270d",
"visibility": "public",
"createdAt": "2026-05-04T09:10:54.707Z",
"updatedAt": "2026-05-04T09:10:54.707Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ioRirBSFjpJD6vqlbyyy",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ioRirBSFjpJD6vqlbyyy/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "qMTN896XzhO8wMX9TLLc",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/"
}
},
{
"object": "site-section",
"id": "sitesc_xgdj8",
"sectionGroup": "sitescg_USLlo",
"icon": "id-card",
"title": "Identity",
"localizedTitle": {
"de": "Identität",
"en": "Identity",
"es": "Identidad",
"fr": "Identité",
"zh": "身份"
},
"description": "Verify customers and partners",
"localizedDescription": {
"de": "Kunden und Partner verifizieren",
"en": "Verify customers and partners",
"es": "Verificar clientes y socios",
"fr": "Vérifier les clients et les partenaires",
"zh": "验证客户和合作伙伴"
},
"path": "identity",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_Kx4Ag",
"path": "en",
"default": true,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "w7NRnYZuokE4h1mm2pJB",
"title": "Identity",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-04-30T09:30:16.308Z",
"updatedAt": "2026-05-01T16:18:16.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/w7NRnYZuokE4h1mm2pJB",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/w7NRnYZuokE4h1mm2pJB/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:28:15.614Z"
},
"revision": "TtVigqSejaizVSjPIUJE",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_YiY3S",
"path": "fr",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "wB2ANcfwHn2chYSRPb3F",
"title": "Identity (FR)",
"emoji": "1f4c4",
"visibility": "public",
"createdAt": "2026-05-04T09:11:07.645Z",
"updatedAt": "2026-05-04T09:11:07.645Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/wB2ANcfwHn2chYSRPb3F",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/wB2ANcfwHn2chYSRPb3F/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "edrQj0SFXviTvSmY52P4",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_UtUff",
"path": "de",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "h5ck7uPaswocTPaQj7sk",
"title": "Identity (DE)",
"emoji": "1f4c4",
"visibility": "public",
"createdAt": "2026-05-04T09:13:05.195Z",
"updatedAt": "2026-05-04T09:13:05.195Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/h5ck7uPaswocTPaQj7sk",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/h5ck7uPaswocTPaQj7sk/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "FKKKmKsZudy9fxV6Xv49",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_jqjxX",
"path": "es",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "ab5DXts4DXiu6hBhbvk6",
"title": "Identity (ES)",
"emoji": "1f4a1",
"visibility": "public",
"createdAt": "2026-05-04T09:11:19.363Z",
"updatedAt": "2026-05-04T09:11:19.363Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ab5DXts4DXiu6hBhbvk6",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ab5DXts4DXiu6hBhbvk6/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "3W6GiHNzGcv2qsGzAC1C",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_cWc1K",
"path": "zh",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "VBajKI7tLXB9WxGD0Wzd",
"title": "Identity (ZH)",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-05-04T09:11:35.937Z",
"updatedAt": "2026-05-04T09:11:35.937Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/VBajKI7tLXB9WxGD0Wzd",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/VBajKI7tLXB9WxGD0Wzd/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "mCstW53oMLqKBc1aKJME",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/"
}
},
{
"object": "site-section",
"id": "sitesc_EK3Mx",
"sectionGroup": "sitescg_USLlo",
"icon": "circles-overlap",
"title": "Connect",
"localizedTitle": {
"de": "Verbinden",
"en": "Connect",
"es": "Conectar",
"fr": "Connexion",
"zh": "连接"
},
"description": "Embed payments in your platform",
"localizedDescription": {
"de": "Zahlungen in Ihre Plattform einbetten",
"en": "Embed payments in your platform",
"es": "Integrar pagos en tu plataforma",
"fr": "Intégrer les paiements dans votre plateforme",
"zh": "将支付嵌入您的平台"
},
"path": "connect",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_0kXWi",
"path": "en",
"default": true,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "Xtfxb7OHGyrdfIsObmnu",
"title": "Connect",
"emoji": "1f58c",
"visibility": "public",
"createdAt": "2026-04-30T10:49:28.075Z",
"updatedAt": "2026-05-01T16:18:12.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/Xtfxb7OHGyrdfIsObmnu",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Xtfxb7OHGyrdfIsObmnu/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:29:20.192Z"
},
"revision": "xKbLPrmLhqnVuUfcOgt5",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 1,
"changeRequestsDraft": 1,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_2v25M",
"path": "fr",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "PpvAcEs0Fyr2To9VrlQB",
"title": "Connect (FR)",
"emoji": "1f58b",
"visibility": "public",
"createdAt": "2026-05-04T09:11:54.339Z",
"updatedAt": "2026-05-04T09:11:54.339Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/PpvAcEs0Fyr2To9VrlQB",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/PpvAcEs0Fyr2To9VrlQB/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "44PotxRFsxR2uZdtGWYI",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_686H8",
"path": "de",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "LyK2rkN1uaGpJlnC46qG",
"title": "Connect (DE)",
"emoji": "1f4dd",
"visibility": "public",
"createdAt": "2026-05-04T09:13:17.102Z",
"updatedAt": "2026-05-04T09:13:17.102Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/LyK2rkN1uaGpJlnC46qG",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/LyK2rkN1uaGpJlnC46qG/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "03SEmY7kwrCL5EYpu9Jk",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_RfRPi",
"path": "es",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "8IzLMeWRXo6KimmTi96R",
"title": "Connect (ES)",
"emoji": "1f4a1",
"visibility": "public",
"createdAt": "2026-05-04T09:12:10.988Z",
"updatedAt": "2026-05-04T09:12:10.988Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/8IzLMeWRXo6KimmTi96R",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/8IzLMeWRXo6KimmTi96R/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "8IzwONUdFIFhMkC78QRn",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_GOGn1",
"path": "zh",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "XjkLp5tfozcCqmrPw6ZU",
"title": "Connect (ZH)",
"emoji": "1f4cc",
"visibility": "public",
"createdAt": "2026-05-04T09:12:24.644Z",
"updatedAt": "2026-05-04T09:12:24.644Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/XjkLp5tfozcCqmrPw6ZU",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/XjkLp5tfozcCqmrPw6ZU/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "YdwhgTKKgpCQddYqG9JU",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/"
}
}
],
"children": [
{
"object": "site-section",
"id": "sitesc_oCmWt",
"sectionGroup": "sitescg_USLlo",
"icon": "credit-card",
"title": "Payments",
"localizedTitle": {
"de": "Zahlungen",
"en": "Payments",
"es": "Pagos",
"fr": "Paiements",
"zh": "支付"
},
"description": "Route, reconcile, and report",
"localizedDescription": {
"de": "Weiterleiten, abstimmen und berichten",
"en": "Route, reconcile, and report",
"es": "Enrutar, conciliar e informar",
"fr": "Acheminer, réconcilier et rapporter",
"zh": "路由、对账和报告"
},
"path": "payments",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_A7GFO",
"path": "en",
"default": true,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "w3LlITSOQye8o4wjsQXV",
"title": "Payments",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-04-30T10:47:06.632Z",
"updatedAt": "2026-05-01T16:47:42.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/w3LlITSOQye8o4wjsQXV",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/w3LlITSOQye8o4wjsQXV/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:27:16.422Z"
},
"revision": "DfklbYYK8im7X8mSQb7U",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 3,
"changeRequestsDraft": 1,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_y3yQ6",
"path": "fr",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "3h1wtBbGcXTLyEoV3qeq",
"title": "Payments (FR)",
"emoji": "1f58b",
"visibility": "public",
"createdAt": "2026-05-04T09:10:27.458Z",
"updatedAt": "2026-05-04T09:10:27.458Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/3h1wtBbGcXTLyEoV3qeq",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/3h1wtBbGcXTLyEoV3qeq/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "baq8cXcmxlaXyGQduh2y",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_rhrla",
"path": "de",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "W4iKHxx7gM4wKWvJzP2f",
"title": "Payments (DE)",
"emoji": "1f4da",
"visibility": "public",
"createdAt": "2026-05-04T09:12:53.503Z",
"updatedAt": "2026-05-04T09:12:53.503Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/W4iKHxx7gM4wKWvJzP2f",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/W4iKHxx7gM4wKWvJzP2f/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "LZyeWISTxI1vkLChjTxz",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ZBZ0j",
"path": "es",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "NXIELvRCE9zYhxFzsOV8",
"title": "Payments (ES)",
"emoji": "270d",
"visibility": "public",
"createdAt": "2026-05-04T09:10:40.746Z",
"updatedAt": "2026-05-04T09:10:40.746Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/NXIELvRCE9zYhxFzsOV8",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/NXIELvRCE9zYhxFzsOV8/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "RY1AzV746PoFWM0j7RfP",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_QcQFf",
"path": "zh",
"default": false,
"section": "sitesc_oCmWt",
"space": {
"object": "space",
"id": "ioRirBSFjpJD6vqlbyyy",
"title": "Payments (ZH)",
"emoji": "270d",
"visibility": "public",
"createdAt": "2026-05-04T09:10:54.707Z",
"updatedAt": "2026-05-04T09:10:54.707Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ioRirBSFjpJD6vqlbyyy",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ioRirBSFjpJD6vqlbyyy/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/payments/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "qMTN896XzhO8wMX9TLLc",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/payments/"
}
},
{
"object": "site-section",
"id": "sitesc_xgdj8",
"sectionGroup": "sitescg_USLlo",
"icon": "id-card",
"title": "Identity",
"localizedTitle": {
"de": "Identität",
"en": "Identity",
"es": "Identidad",
"fr": "Identité",
"zh": "身份"
},
"description": "Verify customers and partners",
"localizedDescription": {
"de": "Kunden und Partner verifizieren",
"en": "Verify customers and partners",
"es": "Verificar clientes y socios",
"fr": "Vérifier les clients et les partenaires",
"zh": "验证客户和合作伙伴"
},
"path": "identity",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_Kx4Ag",
"path": "en",
"default": true,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "w7NRnYZuokE4h1mm2pJB",
"title": "Identity",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-04-30T09:30:16.308Z",
"updatedAt": "2026-05-01T16:18:16.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/w7NRnYZuokE4h1mm2pJB",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/w7NRnYZuokE4h1mm2pJB/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:28:15.614Z"
},
"revision": "TtVigqSejaizVSjPIUJE",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_YiY3S",
"path": "fr",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "wB2ANcfwHn2chYSRPb3F",
"title": "Identity (FR)",
"emoji": "1f4c4",
"visibility": "public",
"createdAt": "2026-05-04T09:11:07.645Z",
"updatedAt": "2026-05-04T09:11:07.645Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/wB2ANcfwHn2chYSRPb3F",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/wB2ANcfwHn2chYSRPb3F/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "edrQj0SFXviTvSmY52P4",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_UtUff",
"path": "de",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "h5ck7uPaswocTPaQj7sk",
"title": "Identity (DE)",
"emoji": "1f4c4",
"visibility": "public",
"createdAt": "2026-05-04T09:13:05.195Z",
"updatedAt": "2026-05-04T09:13:05.195Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/h5ck7uPaswocTPaQj7sk",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/h5ck7uPaswocTPaQj7sk/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "FKKKmKsZudy9fxV6Xv49",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_jqjxX",
"path": "es",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "ab5DXts4DXiu6hBhbvk6",
"title": "Identity (ES)",
"emoji": "1f4a1",
"visibility": "public",
"createdAt": "2026-05-04T09:11:19.363Z",
"updatedAt": "2026-05-04T09:11:19.363Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ab5DXts4DXiu6hBhbvk6",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ab5DXts4DXiu6hBhbvk6/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "3W6GiHNzGcv2qsGzAC1C",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_cWc1K",
"path": "zh",
"default": false,
"section": "sitesc_xgdj8",
"space": {
"object": "space",
"id": "VBajKI7tLXB9WxGD0Wzd",
"title": "Identity (ZH)",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-05-04T09:11:35.937Z",
"updatedAt": "2026-05-04T09:11:35.937Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/VBajKI7tLXB9WxGD0Wzd",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/VBajKI7tLXB9WxGD0Wzd/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/identity/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "mCstW53oMLqKBc1aKJME",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/identity/"
}
},
{
"object": "site-section",
"id": "sitesc_EK3Mx",
"sectionGroup": "sitescg_USLlo",
"icon": "circles-overlap",
"title": "Connect",
"localizedTitle": {
"de": "Verbinden",
"en": "Connect",
"es": "Conectar",
"fr": "Connexion",
"zh": "连接"
},
"description": "Embed payments in your platform",
"localizedDescription": {
"de": "Zahlungen in Ihre Plattform einbetten",
"en": "Embed payments in your platform",
"es": "Integrar pagos en tu plataforma",
"fr": "Intégrer les paiements dans votre plateforme",
"zh": "将支付嵌入您的平台"
},
"path": "connect",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_0kXWi",
"path": "en",
"default": true,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "Xtfxb7OHGyrdfIsObmnu",
"title": "Connect",
"emoji": "1f58c",
"visibility": "public",
"createdAt": "2026-04-30T10:49:28.075Z",
"updatedAt": "2026-05-01T16:18:12.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/Xtfxb7OHGyrdfIsObmnu",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Xtfxb7OHGyrdfIsObmnu/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:29:20.192Z"
},
"revision": "xKbLPrmLhqnVuUfcOgt5",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 1,
"changeRequestsDraft": 1,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_2v25M",
"path": "fr",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "PpvAcEs0Fyr2To9VrlQB",
"title": "Connect (FR)",
"emoji": "1f58b",
"visibility": "public",
"createdAt": "2026-05-04T09:11:54.339Z",
"updatedAt": "2026-05-04T09:11:54.339Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/PpvAcEs0Fyr2To9VrlQB",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/PpvAcEs0Fyr2To9VrlQB/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "44PotxRFsxR2uZdtGWYI",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_686H8",
"path": "de",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "LyK2rkN1uaGpJlnC46qG",
"title": "Connect (DE)",
"emoji": "1f4dd",
"visibility": "public",
"createdAt": "2026-05-04T09:13:17.102Z",
"updatedAt": "2026-05-04T09:13:17.102Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/LyK2rkN1uaGpJlnC46qG",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/LyK2rkN1uaGpJlnC46qG/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "03SEmY7kwrCL5EYpu9Jk",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_RfRPi",
"path": "es",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "8IzLMeWRXo6KimmTi96R",
"title": "Connect (ES)",
"emoji": "1f4a1",
"visibility": "public",
"createdAt": "2026-05-04T09:12:10.988Z",
"updatedAt": "2026-05-04T09:12:10.988Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/8IzLMeWRXo6KimmTi96R",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/8IzLMeWRXo6KimmTi96R/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "8IzwONUdFIFhMkC78QRn",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_GOGn1",
"path": "zh",
"default": false,
"section": "sitesc_EK3Mx",
"space": {
"object": "space",
"id": "XjkLp5tfozcCqmrPw6ZU",
"title": "Connect (ZH)",
"emoji": "1f4cc",
"visibility": "public",
"createdAt": "2026-05-04T09:12:24.644Z",
"updatedAt": "2026-05-04T09:12:24.644Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/XjkLp5tfozcCqmrPw6ZU",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/XjkLp5tfozcCqmrPw6ZU/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/connect/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "YdwhgTKKgpCQddYqG9JU",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/connect/"
}
}
]
},
{
"object": "site-section",
"id": "sitesc_sbz7c",
"icon": "code",
"title": "Developers",
"localizedTitle": {
"de": "Entwickler",
"en": "Developers",
"es": "Desarrolladores",
"fr": "Développeurs",
"zh": "开发者 "
},
"localizedDescription": {},
"path": "developers",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_A7dEx",
"path": "v3-beta",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "ArjuZpkY4MGSoxiPm5Mk",
"title": "Developers V3",
"emoji": "1f4d7",
"visibility": "public",
"createdAt": "2026-04-30T17:32:22.345Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ArjuZpkY4MGSoxiPm5Mk",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ArjuZpkY4MGSoxiPm5Mk/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-beta/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-beta/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T17:38:26.798Z"
},
"revision": "rpuGylZPISvWUYu0KHxq",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 3 (Beta)",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-beta/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_RfRfc",
"path": "v3-fr",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "9sZ4bek0OlJ0AZsWhxt4",
"title": "Developers V3 (FR)",
"emoji": "1f4dd",
"visibility": "public",
"createdAt": "2026-05-04T09:39:31.008Z",
"updatedAt": "2026-05-04T09:39:31.008Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/9sZ4bek0OlJ0AZsWhxt4",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/9sZ4bek0OlJ0AZsWhxt4/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "T8eUcPq4yCbqSyCKt8Uo",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 3 (Bêta)",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_NQNQl",
"path": "v3-de",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "WXPVOGfqTkyxPTSe7EPu",
"title": "Developers V3 (DE)",
"emoji": "1f4d7",
"visibility": "public",
"createdAt": "2026-05-04T09:39:39.676Z",
"updatedAt": "2026-05-04T09:39:39.676Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/WXPVOGfqTkyxPTSe7EPu",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/WXPVOGfqTkyxPTSe7EPu/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "fvZqPlQzEkaAOSWzCsEO",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 3 (Beta)",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_hLhLN",
"path": "v3-es",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "snXrcDDLoeKDLquzeC9a",
"title": "Developers V3 (ES)",
"emoji": "1f4c1",
"visibility": "public",
"createdAt": "2026-05-04T09:39:46.824Z",
"updatedAt": "2026-05-04T09:39:46.824Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/snXrcDDLoeKDLquzeC9a",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/snXrcDDLoeKDLquzeC9a/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "nNQt75vqhiLedH6p2KOC",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Versión 3 (Beta)",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_BbBbh",
"path": "v3-zh",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "l55ylnAhHYnmO1LC2QAc",
"title": "Developers V3 (ZH)",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-05-04T09:39:55.838Z",
"updatedAt": "2026-05-04T09:39:55.838Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/l55ylnAhHYnmO1LC2QAc",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/l55ylnAhHYnmO1LC2QAc/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "0OgaeDRsPiKOzVju10UQ",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "版本 3 (测试版)",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v3-zh/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_L6jIa",
"path": "v2",
"default": true,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "Si95BtOt1VRLWjT7A67V",
"title": "Developers V2",
"emoji": "1f4ca",
"visibility": "public",
"createdAt": "2026-04-30T17:57:17.648Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/Si95BtOt1VRLWjT7A67V",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Si95BtOt1VRLWjT7A67V/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T17:58:18.449Z"
},
"revision": "BgFf4h1cPd29ycl0wgRI",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 1,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 2",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_2v2v9",
"path": "v2-fr",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "6jHoCG3sLHcDD0px67xJ",
"title": "Developers V2 (FR)",
"emoji": "1f4c4",
"visibility": "public",
"createdAt": "2026-05-04T09:35:35.543Z",
"updatedAt": "2026-05-04T09:35:35.543Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/6jHoCG3sLHcDD0px67xJ",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/6jHoCG3sLHcDD0px67xJ/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "oD1lbRjZ7hX4TT6KbAsi",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 2",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_RfRfT",
"path": "v2-de",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "Vs3QFrlFJ5Y3b5V9y7xE",
"title": "Developers V2 (DE)",
"emoji": "1f4d9",
"visibility": "public",
"createdAt": "2026-05-04T09:36:35.141Z",
"updatedAt": "2026-05-04T09:36:35.142Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/Vs3QFrlFJ5Y3b5V9y7xE",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Vs3QFrlFJ5Y3b5V9y7xE/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "ACsAVy1wEBAQVbyUHpjG",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 2",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_GOGOW",
"path": "v2-es",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "I8pLGvdmqapUOLNBlrp6",
"title": "Developers V2 (ES)",
"emoji": "1f58a",
"visibility": "public",
"createdAt": "2026-05-04T09:37:16.606Z",
"updatedAt": "2026-05-04T09:37:16.606Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/I8pLGvdmqapUOLNBlrp6",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/I8pLGvdmqapUOLNBlrp6/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "MWan6R3DJx3WFKLc7SPh",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Versión 2",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_HdHde",
"path": "v2-zh",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "eml0OxXIuDVzao6aoeog",
"title": "Developers V2 (ZH)",
"emoji": "1f58a",
"visibility": "public",
"createdAt": "2026-05-04T09:37:34.580Z",
"updatedAt": "2026-05-04T09:37:34.580Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/eml0OxXIuDVzao6aoeog",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/eml0OxXIuDVzao6aoeog/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "pM7d6eBr3A2s2nmYPYEH",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "版本 2",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v2-zh/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_5uhFw",
"path": "v1",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "xIpOvMEUWFWhHHnP9z9I",
"title": "Developers V1",
"emoji": "1f4da",
"visibility": "public",
"createdAt": "2026-04-30T17:31:27.249Z",
"updatedAt": "2026-05-01T16:18:14.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/xIpOvMEUWFWhHHnP9z9I",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/xIpOvMEUWFWhHHnP9z9I/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T17:36:07.369Z"
},
"revision": "9gCLZnY5JdIqqMNncpjA",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 1",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_vzvzn",
"path": "v1-fr",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "tIlgcXhu4GkYODUhEVGE",
"title": "Developers V1 (FR)",
"emoji": "1f58b",
"visibility": "public",
"createdAt": "2026-05-04T09:38:45.953Z",
"updatedAt": "2026-05-04T09:38:45.953Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/tIlgcXhu4GkYODUhEVGE",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/tIlgcXhu4GkYODUhEVGE/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "DkFXsVJfUM0HX0OgCFQj",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 1",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_oMoMv",
"path": "v1-de",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "LTZK9iFGzY3aN4iaopH3",
"title": "Developers V1 (DE)",
"emoji": "2728",
"visibility": "public",
"createdAt": "2026-05-04T09:39:03.918Z",
"updatedAt": "2026-05-04T09:39:03.918Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/LTZK9iFGzY3aN4iaopH3",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/LTZK9iFGzY3aN4iaopH3/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "5yR2EvmZzdBIUxFqaFfd",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Version 1",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_F1F10",
"path": "v1-es",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "FRPCJTOBFr3qMCcjl8H2",
"title": "Developers V1 (ES)",
"emoji": "1f50f",
"visibility": "public",
"createdAt": "2026-05-04T09:39:12.329Z",
"updatedAt": "2026-05-04T09:39:12.330Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/FRPCJTOBFr3qMCcjl8H2",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/FRPCJTOBFr3qMCcjl8H2/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "vMbYJeVDpvfHuIEFXpsa",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Versión 1",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": true,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_bFbFE",
"path": "v1-zh",
"default": false,
"section": "sitesc_sbz7c",
"space": {
"object": "space",
"id": "Psf5I8KrFOI960qSPplL",
"title": "Developers V1 (ZH)",
"emoji": "1f4c2",
"visibility": "public",
"createdAt": "2026-05-04T09:39:22.308Z",
"updatedAt": "2026-05-04T09:39:22.308Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/Psf5I8KrFOI960qSPplL",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Psf5I8KrFOI960qSPplL/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"revision": "sTAf51FjRsRJATPEaG4W",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "版本 1",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/v1-zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/developers/"
}
},
{
"object": "site-section-group",
"id": "sitescg_6Um9g",
"icon": "compass",
"title": "Guides",
"localizedTitle": {
"de": "Anleitungen",
"en": "Guides",
"es": "Guías",
"zh": "指南"
},
"draft": false,
"sections": [
{
"object": "site-section",
"id": "sitesc_4ji9g",
"sectionGroup": "sitescg_6Um9g",
"icon": "graduation-cap",
"title": "Tutorials",
"localizedTitle": {
"en": "Tutorials",
"es": "Tutoriales",
"fr": "Tutoriels",
"zh": "教程"
},
"description": "Build it, step by step",
"localizedDescription": {
"de": "Schritt für Schritt aufbauen",
"en": "Build it, step by step",
"es": "Constrúyelo, paso a paso",
"fr": "Construisez-le, étape par étape",
"zh": "一步一步地构建"
},
"path": "tutorials",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_s5CUG",
"path": "en",
"default": true,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "Nankrp40VchJsUblU6h6",
"title": "Tutorials",
"emoji": "1f4cb",
"visibility": "public",
"createdAt": "2026-04-30T10:51:47.765Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/Nankrp40VchJsUblU6h6",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Nankrp40VchJsUblU6h6/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:30:59.907Z"
},
"revision": "2KDvSaP3WKEG8IKvDUA8",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_y3yQd",
"path": "fr",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "CSBRCiXJmvhI6EPVWKMJ",
"title": "Tutorials (FR)",
"emoji": "1f4d2",
"visibility": "public",
"createdAt": "2026-05-04T09:23:35.705Z",
"updatedAt": "2026-05-04T09:23:35.705Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/CSBRCiXJmvhI6EPVWKMJ",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/CSBRCiXJmvhI6EPVWKMJ/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "Am5aTtVkfKa2XL4lVbXR",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_KxKYM",
"path": "de",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "xH5mUm0LsfOX2gpxZ4b3",
"title": "Tutorials (DE)",
"emoji": "1f3a8",
"visibility": "public",
"createdAt": "2026-05-04T09:24:04.001Z",
"updatedAt": "2026-05-04T09:24:04.001Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/xH5mUm0LsfOX2gpxZ4b3",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/xH5mUm0LsfOX2gpxZ4b3/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "mHQHV05QciJi2WcxdUoe",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_xTxiK",
"path": "es",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "EzdXMUubPs7NhzYSiKRb",
"title": "Tutorials (ES)",
"emoji": "1f4d9",
"visibility": "public",
"createdAt": "2026-05-04T09:24:22.964Z",
"updatedAt": "2026-05-04T09:24:22.964Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/EzdXMUubPs7NhzYSiKRb",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/EzdXMUubPs7NhzYSiKRb/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "rR13q1WqMHysG7XTTzdE",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ENEBM",
"path": "zh",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "UETwJZhh4yMbZw33C8YR",
"title": "Tutorials (ZH)",
"emoji": "1f3a8",
"visibility": "public",
"createdAt": "2026-05-04T09:24:40.474Z",
"updatedAt": "2026-05-04T09:24:40.474Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/UETwJZhh4yMbZw33C8YR",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/UETwJZhh4yMbZw33C8YR/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "NaCRuLg58cUQmxhiVQ9l",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/"
}
},
{
"object": "site-section",
"id": "sitesc_SsjnF",
"sectionGroup": "sitescg_6Um9g",
"icon": "life-ring",
"title": "Help Center",
"localizedTitle": {
"de": "Hilfecenter",
"en": "Help Center",
"es": "Centro de ayuda",
"fr": "Centre d'aide",
"zh": "帮助中心"
},
"description": "Answers to common questions",
"localizedDescription": {
"de": "Antworten auf häufige Fragen",
"en": "Answers to common questions",
"es": "Respuestas a preguntas frecuentes",
"fr": "Réponses aux questions fréquentes",
"zh": "常见问题解答 "
},
"path": "help-center",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_Sao6e",
"path": "en",
"default": true,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "NA4Ikc8fQtsXC5U53xJu",
"title": "Troubleshooting",
"emoji": "1f50f",
"visibility": "public",
"createdAt": "2026-04-30T10:52:27.026Z",
"updatedAt": "2026-05-01T16:44:26.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/NA4Ikc8fQtsXC5U53xJu",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/NA4Ikc8fQtsXC5U53xJu/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:31:43.416Z"
},
"revision": "tR4y7rNKHUq4HA1yzbjG",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_4y4Nl",
"path": "fr",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "r00bHSxdrv7jKbcEzkXb",
"title": "Troubleshooting (FR)",
"emoji": "1f4c9",
"visibility": "public",
"createdAt": "2026-05-04T09:26:04.953Z",
"updatedAt": "2026-05-04T09:26:04.953Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/r00bHSxdrv7jKbcEzkXb",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/r00bHSxdrv7jKbcEzkXb/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "uuWaJd2C39eju3YyKXbH",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_SaS97",
"path": "de",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "7r9ZmopttQOlz2yBT0Sx",
"title": "Troubleshooting (DE)",
"emoji": "1f4c3",
"visibility": "public",
"createdAt": "2026-05-04T09:26:27.370Z",
"updatedAt": "2026-05-04T09:26:27.370Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/7r9ZmopttQOlz2yBT0Sx",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/7r9ZmopttQOlz2yBT0Sx/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "20UGbjOh32RFUBgi304A",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_zRzgG",
"path": "es",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "9wozgNBjifHxWCfYBM5b",
"title": "Troubleshooting (ES)",
"emoji": "2728",
"visibility": "public",
"createdAt": "2026-05-04T09:26:41.331Z",
"updatedAt": "2026-05-04T09:26:41.331Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/9wozgNBjifHxWCfYBM5b",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/9wozgNBjifHxWCfYBM5b/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "mBMLEue2v7xqYPgmMBs3",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ljlK3",
"path": "zh",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "MCg1B8wkyVBjEzlKnR26",
"title": "Troubleshooting (ZH)",
"emoji": "1f4c9",
"visibility": "public",
"createdAt": "2026-05-04T09:26:55.505Z",
"updatedAt": "2026-05-04T09:26:55.505Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/MCg1B8wkyVBjEzlKnR26",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/MCg1B8wkyVBjEzlKnR26/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "hg4Kj55lEUhn1In6jjPr",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/"
}
},
{
"object": "site-section",
"id": "sitesc_goZzh",
"sectionGroup": "sitescg_6Um9g",
"icon": "puzzle-piece",
"title": "Integrations",
"localizedTitle": {
"de": "Integrationen",
"en": "Integrations",
"es": "Integraciones",
"fr": "Intégrations",
"zh": "集成"
},
"description": "Bring your existing tools",
"localizedDescription": {
"de": "Bringen Sie Ihre vorhandenen Tools mit",
"en": "Bring your existing tools",
"es": "Trae tus herramientas existentes",
"fr": "Intégrez vos outils existants",
"zh": "集成您现有的工具 "
},
"path": "integrations",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_gPqNp",
"path": "en",
"default": true,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "MBT3EDUK7DzXmR0k9cje",
"title": "Integrations",
"emoji": "1f58b",
"visibility": "public",
"createdAt": "2026-04-30T10:53:05.479Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/MBT3EDUK7DzXmR0k9cje",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/MBT3EDUK7DzXmR0k9cje/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:35:18.306Z"
},
"revision": "ZvjAcj2EJCBSa0HPu9HI",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_wSwVm",
"path": "fr",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "jV3swX21EFN30WcbGloj",
"title": "Integrations (FR)",
"emoji": "1f4da",
"visibility": "public",
"createdAt": "2026-05-04T09:27:36.278Z",
"updatedAt": "2026-05-04T09:27:36.278Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/jV3swX21EFN30WcbGloj",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/jV3swX21EFN30WcbGloj/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "BIUH71Hprwwbgks1ckzD",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ieiDC",
"path": "de",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "ozHP4EI2UripRSRriFMi",
"title": "Integrations (DE)",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-05-04T09:28:08.019Z",
"updatedAt": "2026-05-04T09:28:08.019Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ozHP4EI2UripRSRriFMi",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ozHP4EI2UripRSRriFMi/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "UJumLqhVnFyEtRJmebg7",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_L6LqJ",
"path": "es",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "qi7no5xCrY4m4KpA7oc4",
"title": "Integrations (ES)",
"emoji": "1f4d8",
"visibility": "public",
"createdAt": "2026-05-04T09:28:27.474Z",
"updatedAt": "2026-05-04T09:28:27.474Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/qi7no5xCrY4m4KpA7oc4",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/qi7no5xCrY4m4KpA7oc4/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "NAwp0WPAjcvmov92tLy3",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_3D3cr",
"path": "zh",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "594Gdtjr6dKzB11L4Wz1",
"title": "Integrations (ZH)",
"emoji": "1f4c3",
"visibility": "public",
"createdAt": "2026-05-04T09:28:47.076Z",
"updatedAt": "2026-05-04T09:28:47.076Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/594Gdtjr6dKzB11L4Wz1",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/594Gdtjr6dKzB11L4Wz1/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "wKPZlp4cZ6DAW5MqFiQX",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/"
}
}
],
"children": [
{
"object": "site-section",
"id": "sitesc_4ji9g",
"sectionGroup": "sitescg_6Um9g",
"icon": "graduation-cap",
"title": "Tutorials",
"localizedTitle": {
"en": "Tutorials",
"es": "Tutoriales",
"fr": "Tutoriels",
"zh": "教程"
},
"description": "Build it, step by step",
"localizedDescription": {
"de": "Schritt für Schritt aufbauen",
"en": "Build it, step by step",
"es": "Constrúyelo, paso a paso",
"fr": "Construisez-le, étape par étape",
"zh": "一步一步地构建"
},
"path": "tutorials",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_s5CUG",
"path": "en",
"default": true,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "Nankrp40VchJsUblU6h6",
"title": "Tutorials",
"emoji": "1f4cb",
"visibility": "public",
"createdAt": "2026-04-30T10:51:47.765Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/Nankrp40VchJsUblU6h6",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/Nankrp40VchJsUblU6h6/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:30:59.907Z"
},
"revision": "2KDvSaP3WKEG8IKvDUA8",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_y3yQd",
"path": "fr",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "CSBRCiXJmvhI6EPVWKMJ",
"title": "Tutorials (FR)",
"emoji": "1f4d2",
"visibility": "public",
"createdAt": "2026-05-04T09:23:35.705Z",
"updatedAt": "2026-05-04T09:23:35.705Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/CSBRCiXJmvhI6EPVWKMJ",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/CSBRCiXJmvhI6EPVWKMJ/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "Am5aTtVkfKa2XL4lVbXR",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_KxKYM",
"path": "de",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "xH5mUm0LsfOX2gpxZ4b3",
"title": "Tutorials (DE)",
"emoji": "1f3a8",
"visibility": "public",
"createdAt": "2026-05-04T09:24:04.001Z",
"updatedAt": "2026-05-04T09:24:04.001Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/xH5mUm0LsfOX2gpxZ4b3",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/xH5mUm0LsfOX2gpxZ4b3/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "mHQHV05QciJi2WcxdUoe",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_xTxiK",
"path": "es",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "EzdXMUubPs7NhzYSiKRb",
"title": "Tutorials (ES)",
"emoji": "1f4d9",
"visibility": "public",
"createdAt": "2026-05-04T09:24:22.964Z",
"updatedAt": "2026-05-04T09:24:22.964Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/EzdXMUubPs7NhzYSiKRb",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/EzdXMUubPs7NhzYSiKRb/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "rR13q1WqMHysG7XTTzdE",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ENEBM",
"path": "zh",
"default": false,
"section": "sitesc_4ji9g",
"space": {
"object": "space",
"id": "UETwJZhh4yMbZw33C8YR",
"title": "Tutorials (ZH)",
"emoji": "1f3a8",
"visibility": "public",
"createdAt": "2026-05-04T09:24:40.474Z",
"updatedAt": "2026-05-04T09:24:40.474Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/UETwJZhh4yMbZw33C8YR",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/UETwJZhh4yMbZw33C8YR/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "NaCRuLg58cUQmxhiVQ9l",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/tutorials/"
}
},
{
"object": "site-section",
"id": "sitesc_SsjnF",
"sectionGroup": "sitescg_6Um9g",
"icon": "life-ring",
"title": "Help Center",
"localizedTitle": {
"de": "Hilfecenter",
"en": "Help Center",
"es": "Centro de ayuda",
"fr": "Centre d'aide",
"zh": "帮助中心"
},
"description": "Answers to common questions",
"localizedDescription": {
"de": "Antworten auf häufige Fragen",
"en": "Answers to common questions",
"es": "Respuestas a preguntas frecuentes",
"fr": "Réponses aux questions fréquentes",
"zh": "常见问题解答 "
},
"path": "help-center",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_Sao6e",
"path": "en",
"default": true,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "NA4Ikc8fQtsXC5U53xJu",
"title": "Troubleshooting",
"emoji": "1f50f",
"visibility": "public",
"createdAt": "2026-04-30T10:52:27.026Z",
"updatedAt": "2026-05-01T16:44:26.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/NA4Ikc8fQtsXC5U53xJu",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/NA4Ikc8fQtsXC5U53xJu/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:31:43.416Z"
},
"revision": "tR4y7rNKHUq4HA1yzbjG",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_4y4Nl",
"path": "fr",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "r00bHSxdrv7jKbcEzkXb",
"title": "Troubleshooting (FR)",
"emoji": "1f4c9",
"visibility": "public",
"createdAt": "2026-05-04T09:26:04.953Z",
"updatedAt": "2026-05-04T09:26:04.953Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/r00bHSxdrv7jKbcEzkXb",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/r00bHSxdrv7jKbcEzkXb/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "uuWaJd2C39eju3YyKXbH",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_SaS97",
"path": "de",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "7r9ZmopttQOlz2yBT0Sx",
"title": "Troubleshooting (DE)",
"emoji": "1f4c3",
"visibility": "public",
"createdAt": "2026-05-04T09:26:27.370Z",
"updatedAt": "2026-05-04T09:26:27.370Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/7r9ZmopttQOlz2yBT0Sx",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/7r9ZmopttQOlz2yBT0Sx/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "20UGbjOh32RFUBgi304A",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_zRzgG",
"path": "es",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "9wozgNBjifHxWCfYBM5b",
"title": "Troubleshooting (ES)",
"emoji": "2728",
"visibility": "public",
"createdAt": "2026-05-04T09:26:41.331Z",
"updatedAt": "2026-05-04T09:26:41.331Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/9wozgNBjifHxWCfYBM5b",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/9wozgNBjifHxWCfYBM5b/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "mBMLEue2v7xqYPgmMBs3",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ljlK3",
"path": "zh",
"default": false,
"section": "sitesc_SsjnF",
"space": {
"object": "space",
"id": "MCg1B8wkyVBjEzlKnR26",
"title": "Troubleshooting (ZH)",
"emoji": "1f4c9",
"visibility": "public",
"createdAt": "2026-05-04T09:26:55.505Z",
"updatedAt": "2026-05-04T09:26:55.505Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/MCg1B8wkyVBjEzlKnR26",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/MCg1B8wkyVBjEzlKnR26/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "hg4Kj55lEUhn1In6jjPr",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/help-center/"
}
},
{
"object": "site-section",
"id": "sitesc_goZzh",
"sectionGroup": "sitescg_6Um9g",
"icon": "puzzle-piece",
"title": "Integrations",
"localizedTitle": {
"de": "Integrationen",
"en": "Integrations",
"es": "Integraciones",
"fr": "Intégrations",
"zh": "集成"
},
"description": "Bring your existing tools",
"localizedDescription": {
"de": "Bringen Sie Ihre vorhandenen Tools mit",
"en": "Bring your existing tools",
"es": "Trae tus herramientas existentes",
"fr": "Intégrez vos outils existants",
"zh": "集成您现有的工具 "
},
"path": "integrations",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_gPqNp",
"path": "en",
"default": true,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "MBT3EDUK7DzXmR0k9cje",
"title": "Integrations",
"emoji": "1f58b",
"visibility": "public",
"createdAt": "2026-04-30T10:53:05.479Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/MBT3EDUK7DzXmR0k9cje",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/MBT3EDUK7DzXmR0k9cje/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:35:18.306Z"
},
"revision": "ZvjAcj2EJCBSa0HPu9HI",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_wSwVm",
"path": "fr",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "jV3swX21EFN30WcbGloj",
"title": "Integrations (FR)",
"emoji": "1f4da",
"visibility": "public",
"createdAt": "2026-05-04T09:27:36.278Z",
"updatedAt": "2026-05-04T09:27:36.278Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/jV3swX21EFN30WcbGloj",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/jV3swX21EFN30WcbGloj/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "BIUH71Hprwwbgks1ckzD",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ieiDC",
"path": "de",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "ozHP4EI2UripRSRriFMi",
"title": "Integrations (DE)",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-05-04T09:28:08.019Z",
"updatedAt": "2026-05-04T09:28:08.019Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ozHP4EI2UripRSRriFMi",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ozHP4EI2UripRSRriFMi/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "UJumLqhVnFyEtRJmebg7",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_L6LqJ",
"path": "es",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "qi7no5xCrY4m4KpA7oc4",
"title": "Integrations (ES)",
"emoji": "1f4d8",
"visibility": "public",
"createdAt": "2026-05-04T09:28:27.474Z",
"updatedAt": "2026-05-04T09:28:27.474Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/qi7no5xCrY4m4KpA7oc4",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/qi7no5xCrY4m4KpA7oc4/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "NAwp0WPAjcvmov92tLy3",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_3D3cr",
"path": "zh",
"default": false,
"section": "sitesc_goZzh",
"space": {
"object": "space",
"id": "594Gdtjr6dKzB11L4Wz1",
"title": "Integrations (ZH)",
"emoji": "1f4c3",
"visibility": "public",
"createdAt": "2026-05-04T09:28:47.076Z",
"updatedAt": "2026-05-04T09:28:47.076Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/594Gdtjr6dKzB11L4Wz1",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/594Gdtjr6dKzB11L4Wz1/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "wKPZlp4cZ6DAW5MqFiQX",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/integrations/"
}
}
]
},
{
"object": "site-section",
"id": "sitesc_zWtp2",
"icon": "clock-rotate-left",
"title": "Changelog",
"localizedTitle": {
"de": "Änderungsprotokoll",
"en": "Changelog",
"es": "Registro de cambios",
"fr": "Journal des modifications",
"zh": "更新日志"
},
"localizedDescription": {},
"path": "changelog",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_zRL4Q",
"path": "en",
"default": true,
"section": "sitesc_zWtp2",
"space": {
"object": "space",
"id": "ErQsbFsgm6eg9BApdmPl",
"title": "Changelog",
"emoji": "1f4c9",
"visibility": "public",
"createdAt": "2026-04-30T10:53:25.091Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/ErQsbFsgm6eg9BApdmPl",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/ErQsbFsgm6eg9BApdmPl/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:36:06.074Z"
},
"revision": "JEWwnQw2lTw9QhvYNU6c",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 2,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_UtUfg",
"path": "fr",
"default": false,
"section": "sitesc_zWtp2",
"space": {
"object": "space",
"id": "HDMwB2W6Hfo0ngmeUPzN",
"title": "Changelog (FR)",
"emoji": "1f4bb",
"visibility": "public",
"createdAt": "2026-05-04T09:30:44.806Z",
"updatedAt": "2026-05-04T09:30:44.806Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/HDMwB2W6Hfo0ngmeUPzN",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/HDMwB2W6Hfo0ngmeUPzN/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "Q9k2a0qvpzS533Aa4zXQ",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_YiY3y",
"path": "de",
"default": false,
"section": "sitesc_zWtp2",
"space": {
"object": "space",
"id": "0V60HjynvrW2SRx1hMa4",
"title": "Changelog (DE)",
"emoji": "1f4d2",
"visibility": "public",
"createdAt": "2026-05-04T09:31:09.674Z",
"updatedAt": "2026-05-04T09:31:09.674Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/0V60HjynvrW2SRx1hMa4",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/0V60HjynvrW2SRx1hMa4/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "mJvVIDhstblAWskGbUEu",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_686H6",
"path": "es",
"default": false,
"section": "sitesc_zWtp2",
"space": {
"object": "space",
"id": "PGBkdp3S9jZN7HQAQAkK",
"title": "Changelog (ES)",
"emoji": "2728",
"visibility": "public",
"createdAt": "2026-05-04T09:31:24.733Z",
"updatedAt": "2026-05-04T09:31:24.733Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/PGBkdp3S9jZN7HQAQAkK",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/PGBkdp3S9jZN7HQAQAkK/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "gC4PTgNKv9akI4qY1Uc0",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_kpkpQ",
"path": "zh",
"default": false,
"section": "sitesc_zWtp2",
"space": {
"object": "space",
"id": "NRDsotL9NwlFxV8alp0b",
"title": "Changelog (ZH)",
"emoji": "1f4c9",
"visibility": "public",
"createdAt": "2026-05-04T09:31:44.908Z",
"updatedAt": "2026-05-04T09:31:44.909Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/NRDsotL9NwlFxV8alp0b",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/NRDsotL9NwlFxV8alp0b/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "doRKUZsQHATqNSUE4f2p",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/changelog/"
}
},
{
"object": "site-section",
"id": "sitesc_lv6rO",
"icon": "handshake-angle",
"title": "Partners",
"localizedTitle": {
"de": "Partner",
"en": "Partners",
"es": "Socios",
"fr": "Partenaires",
"zh": "合作伙伴"
},
"localizedDescription": {},
"path": "partners",
"default": false,
"draft": false,
"siteSpaces": [
{
"object": "site-space",
"id": "sitesp_lj9P0",
"path": "en",
"default": true,
"section": "sitesc_lv6rO",
"space": {
"object": "space",
"id": "R0VawBV5xcQ4exP2PlWS",
"title": "Partners",
"emoji": "1f58c",
"visibility": "public",
"createdAt": "2026-04-30T10:53:46.425Z",
"updatedAt": "2026-05-01T16:18:13.000Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/R0VawBV5xcQ4exP2PlWS",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/R0VawBV5xcQ4exP2PlWS/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/partners/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"gitSync": {
"installationProvider": "github",
"integration": "github",
"url": "https://github.com/GitbookIO/evolve-demo/blob/main",
"updatedAt": "2026-04-30T11:36:53.556Z"
},
"revision": "rPKrivyZAwhiZO1kDCNI",
"defaultLevel": "inherit",
"language": "en",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "English",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ClCl2",
"path": "fr",
"default": false,
"section": "sitesc_lv6rO",
"space": {
"object": "space",
"id": "WKm4X1yjZ9wQmYDcbTSb",
"title": "Partners (FR)",
"emoji": "1f4d2",
"visibility": "public",
"createdAt": "2026-05-04T09:32:14.563Z",
"updatedAt": "2026-05-04T09:32:14.563Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/WKm4X1yjZ9wQmYDcbTSb",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/WKm4X1yjZ9wQmYDcbTSb/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/fr/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/partners/fr/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "4hQowXFlQOHf0YcIqe7d",
"defaultLevel": "inherit",
"language": "fr",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Français",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/fr/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_8A8AM",
"path": "de",
"default": false,
"section": "sitesc_lv6rO",
"space": {
"object": "space",
"id": "OfL9ieeGF3kPZwl7SXST",
"title": "Partners (DE)",
"emoji": "1f4d7",
"visibility": "public",
"createdAt": "2026-05-04T09:32:45.289Z",
"updatedAt": "2026-05-04T09:32:45.289Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/OfL9ieeGF3kPZwl7SXST",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/OfL9ieeGF3kPZwl7SXST/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/de/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/partners/de/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "uP1XCDHkh1mjsVkWCU0V",
"defaultLevel": "inherit",
"language": "de",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Deutsch",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/de/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_y3y3g",
"path": "es",
"default": false,
"section": "sitesc_lv6rO",
"space": {
"object": "space",
"id": "blgn3jJmkMqKvC2PV1f5",
"title": "Partners (ES)",
"emoji": "1f4bb",
"visibility": "public",
"createdAt": "2026-05-04T09:33:00.615Z",
"updatedAt": "2026-05-04T09:33:00.615Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/blgn3jJmkMqKvC2PV1f5",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/blgn3jJmkMqKvC2PV1f5/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/es/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/partners/es/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "FzLlX4VDkJCPXiiHvazC",
"defaultLevel": "inherit",
"language": "es",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "Español",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/es/"
},
"hidden": false
},
{
"object": "site-space",
"id": "sitesp_ZBZBL",
"path": "zh",
"default": false,
"section": "sitesc_lv6rO",
"space": {
"object": "space",
"id": "OEHIhdcsVdFEu4n2FqWd",
"title": "Partners (ZH)",
"emoji": "1f4d6",
"visibility": "public",
"createdAt": "2026-05-04T09:33:21.654Z",
"updatedAt": "2026-05-04T09:33:21.654Z",
"editMode": "locked",
"internal_poweredByV2": false,
"internal_singleWebsocket": false,
"urls": {
"location": "/spaces/OEHIhdcsVdFEu4n2FqWd",
"app": "https://app.gitbook.com/o/2DnmWBpytIOUKeXExonU/s/OEHIhdcsVdFEu4n2FqWd/",
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/zh/",
"public": "https://enterprise-demos.gitbook.io/evolve-docs/partners/zh/"
},
"organization": "2DnmWBpytIOUKeXExonU",
"parent": "KkhNnuPnXycLk53aubXJ",
"revision": "ZbbZPvecGU2BxJxWb3sq",
"defaultLevel": "inherit",
"language": "zh",
"comments": 0,
"changeRequests": 0,
"changeRequestsDraft": 0,
"changeRequestsOpen": 0,
"permissions": {
"view": true,
"access": true,
"admin": true,
"viewInviteLinks": true,
"edit": true,
"triggerGitSync": true,
"comment": true,
"merge": true,
"review": true,
"installIntegration": true
},
"mergeRules": {
"type": "inherit"
}
},
"title": "中文",
"localizedTitle": {},
"draft": false,
"hasAdvancedCustomizationFeature": false,
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/zh/"
},
"hidden": false
}
],
"urls": {
"published": "https://enterprise-demos.gitbook.io/evolve-docs/partners/"
}
}
]
}
references/git-sync-handoff.md
# Git Sync handoff
Git Sync can now be configured for an entire **site** in one pass, mapping every space to a directory in a single repo/branch via `gitbook-docs.yaml`. **This is the default workflow — always reach for site-wide Git Sync first.** Per-space ("individual space") Git Sync still exists, but treat it as a fallback for the specific case where one space needs to live in a different repo or branch than the rest of the site (e.g. a private space, or content that must stay out of the public docs repo).
GitBook's API does not let you fully self-serve this setup yet: connecting the GitHub/GitLab account (OAuth), picking the repository, and choosing the branch and initial sync direction are UI-only. There is an API operation, `installGitSyncProviderOnTarget`, that accepts either a site or a space as its target — but as of this writing it isn't exposed through the GitBook MCP server, and the account-connection step still has to happen in the app first regardless. GitBook has said they're exploring letting a connection be set up once and reused across the API, but that isn't available yet. Don't build a flow around it — check `search`/`describe_operation` for `installGitSyncProviderOnTarget` if you want to confirm current availability, but default to the UI handoff below.
This file is the template for the user-facing instructions you generate so the user can finish wiring it up themselves.
## Pre-handoff checklist
Before generating the handoff, make sure all of this is true:
- The local repo is committed and pushed to a remote (GitHub or GitLab).
- You know the site's **Project directory** — the path in the repo where `gitbook-docs.yaml` should live. Leave this blank for a repo root; set it only when the docs live in a subdirectory of a larger monorepo (e.g. alongside application code).
- You know each space's **content directory** — the path (relative to the project directory, or from the repo root with a leading `/`) that maps to that space. This is what you'd pre-author into `gitbook-docs.yaml` if you scaffolded the repo yourself; see `content-configuration.md` conventions below.
- You know which branch the user wants to sync from (default: `main`).
- The site exists in GitBook (you have its dashboard URL).
- You know whether the repo content should overwrite GitBook (repo is the source of truth — the normal case for a fresh scaffold) or whether GitBook's existing content should overwrite the repo.
If any of those is false, finish that prep work first. Don't ask the user to context-switch into the GitBook UI before everything is ready.
**If you scaffolded the repo yourself**, pre-author `gitbook-docs.yaml` at the project directory with each space already mapped (see the example below) and commit/push it before handoff. GitBook reads the existing mapping on first sync, so the user has less to fill in by hand during **Map your spaces**. Verify the mapping still matches what you tell them to enter in the UI — GitBook creates or updates `gitbook-docs.yaml` when it saves the content mapping, so the two need to agree.
```yaml
# gitbook-docs.yaml, at the project directory (repo root if unset)
$schema: https://api.gitbook.com/openapi.yaml#/components/schemas/GitSyncSiteConfig
site:
title: [Site title]
structure:
- type: space
key: guides
title: Guides
path: guides
content:
directory: ./guides
- type: space
key: api-reference
title: API Reference
path: api-reference
content:
directory: ./api-reference
```
## The handoff template
Fill in the bracketed values and present this to the user:
---
> ## Setting up Git Sync — one short step in the GitBook UI
>
> Everything else is done. To finish, you need to connect the site to your repo and confirm which directory maps to which space. This is the one part that GitBook's API doesn't expose, so it has to happen in the UI. It takes about a minute for the whole site — you don't need to repeat this per space.
>
> ### Open Git Sync for the site
>
> 1. Open the site dashboard: **[https://app.gitbook.com/o/<orgId>/sites/<siteId>](https://app.gitbook.com/...)**
> 2. Open **Git Sync** in the sidebar.
> 3. Connect **[GitHub / GitLab]**.
> - **GitHub**: authorize the GitBook app if prompted. If you hit a "potential duplicated accounts" error, that means your GitHub account is already linked to a different GitBook user — log out and sign in with GitHub directly to find which account, then unlink it in Settings before retrying.
> - **GitLab**: create a Personal access token in GitLab (user settings → Access tokens) with the `api`, `read_repository`, and `write_repository` scopes, then paste it in. If the token has a role, use `Maintainer` or `Admin`.
> 4. Under **Source repository**, select **`[owner/repo]`** and branch **`[main]`**. If the branch doesn't exist yet, GitBook creates it on first sync.
> 5. Choose the initial sync direction: **[GitHub/GitLab → GitBook]**, since the repo is the source of truth. *(Only pick "Swap direction" if GitBook's existing content should overwrite the repo instead — double-check before confirming, this isn't easily undone.)*
> 6. Click **Show advanced options** and set **Project directory** to **`[repo root, or subdirectory if this is a monorepo]`**.
> 7. Under **Content mapping**, map each space to its directory:
> - **`[Space 1 Title]`** → **`[./guides]`**
> - **`[Space 2 Title]`** → **`[./api-reference]`**
> - *(repeat for each space)*
> 8. Click **Sync** and wait for the import to finish.
>
> Once you've done this, let me know and I'll verify the site's sync state and apply the branding settings.
---
## When a space needs its own repo or branch
Use individual space Git Sync only when one space genuinely can't share the site's repo/branch — for example, a private space that must stay out of the public docs repo. Two ways in:
- **Excluding a space already in site-wide Git Sync**: in the site's Git Sync content mapping, click the remove icon next to that space. GitBook then asks whether to (a) point it at the site's own repo/branch (adds it back into site-wide sync) or (b) point it at an independent repo/branch (space-level sync).
- **A space that was never part of site-wide sync**: in that space, click **Set up** next to **Git Sync** in the space header, then choose **GitHub Sync** or **GitLab Sync** from the provider list and follow the same connect → select repo/branch → direction → project directory steps, scoped to just that space.
Don't default to this path — it fragments where content lives and each space then needs its own handoff and verification. Reach for it only when the user has a concrete reason a space can't share the site's repo.
## Verifying afterwards
After the user confirms they're done, verify each space programmatically (there's no site-level Git Sync status endpoint yet — check per space):
```bash
for space_id in $SPACE_IDS; do
echo "=== $space_id ==="
curl -s -H "Authorization: Bearer $GITBOOK_TOKEN" \
https://api.gitbook.com/v1/spaces/$space_id/git/info
done
```
For each space, expect a 200 with `{repoName, installationProvider, integration, url, updatedAt}`. A 404 means sync isn't set up — point the user back to the relevant step.
Common issues:
- **"Repository not found"** in the UI — the GitBook app doesn't have access. For GitHub: Settings → Applications → GitBook → Configure, and grant access to the right repo. For GitLab: confirm the access token has `api`, `read_repository`, `write_repository`.
- **Initial sync direction wrong** — if the user picks "GitBook → GitHub/GitLab" by mistake when the repo's content should have won, GitBook will overwrite the repo with GitBook's (possibly empty) content. They can't undo this except by `git revert`. Be very explicit in the instructions about direction.
- **Project directory vs. content mapping confusion** — the site's **Project directory** is only where `gitbook-docs.yaml` lives; each space's actual content directory is set separately in **Content mapping**. Getting these swapped is the most common setup mistake with the new site-wide flow. They can fix the mapping afterward in the site's Git Sync settings, which rewrites `gitbook-docs.yaml`.
- **Protected branch push errors** — the GitBook app needs to bypass branch protection rules to push. On GitHub: repo settings → branch protection → allow `gitbook-com` to bypass. On GitLab: if `main` is protected, sync from a separate branch and merge that into `main` manually.
## When there is no remote (local-only)
Git Sync requires a hosted remote — GitBook reaches the repo over the public Git provider APIs, not via direct file access. If the user explicitly chose local-only, they'll need to either push to a hosted remote later (GitBook supports private repos) or skip Git Sync and use the API content path. Tell them this clearly rather than implying Git Sync is possible without a remote.
## Other things worth mentioning in the handoff
- **PR previews (GitHub only)**: once Git Sync is set up, PRs against the synced branch automatically get a status check with a preview link, as long as the GitBook GitHub app has read access to PRs. Previews are skipped by default for PRs from forks (a security default, configurable in Git Sync settings) and aren't available on sites behind authenticated access.
- **Enterprise IP allowlisting**: if the user's network restricts outbound traffic, they'll need to allow these five IPs: `34.136.22.210`, `34.29.189.57`, `35.223.181.150`, `34.72.115.112`, `136.116.236.109`.
- **Commit messages**: GitBook's default export commit message is `GITBOOK-<num>: <change request subject>`. This is customizable in the Git Sync advanced options — mention it if the user has commit message conventions to follow.
references/migration-from-other-platforms.md
# Migration from other platforms
Migrating an existing docs site to GitBook isn't the same workflow as building from scratch — it has its own pitfalls and its own opportunities to do less work for a better outcome. This reference covers the platforms most commonly migrated *from* (Mintlify, Docusaurus, GitBook v1, raw ReadTheDocs/HTML scrapes), the pre-flight checks that catch missing content, the format-pass that catches conversion artifacts, and the anchor-pages strategy that produces a quality result without hand-crafting the long tail.
Read this **before** you start converting files. The biggest content-quality failures in migrations come from skipping the pre-flight, not from any specific bad conversion.
## Pre-flight: understand what you actually have
Before generating a single GitBook file, do these:
### 1. Fetch the rendered source
If the original site is live, fetch the rendered landing page for each section/space-equivalent. The user's IA is *visible* on the rendered site in ways that aren't visible in a folder of markdown — card layouts, callout densities, pinned sections, "what's new" blocks, persona switchers, navigation bar links.
```bash
# At minimum, save the rendered homepage and one representative deep page from each section
curl -sL "https://docs.example.com/" -o /tmp/source-home.html
curl -sL "https://docs.example.com/api-reference/" -o /tmp/source-api-home.html
```
These get used as the spec for what to mirror, in Step 5 below.
### 2. Catalog the source's information architecture
For each space-equivalent in the source (whatever the platform calls them — categories, sidebars, sections), write down:
- The **top-level pages** and the **named groups** they're organized into. Group names are user-visible and almost always carry meaning that's not encoded in folder names.
- The **landing-page structure** for each space — which cards/links/sections exist on the homepage, in what order, with what icons.
- The **navigation chrome** — header links, footer links, social icons, banner announcements.
This is the input to GitBook structure design. **It is not optional.** Skipping this and inferring structure from folders gives you a docs site that looks like a file tree, not a product.
### 3. Sample-check the bulk export against the rendered site
If the user's migration source is a bulk export (Mintlify's `llms-full.txt`, ReadTheDocs `.zip`, GitBook v1 export), pick three pages — homepage, a marquee how-to, and an API reference page — and **compare the export to the rendered original**. Things that often go missing:
- Custom components (Mintlify's `<Card>`, `<CardGroup>`, `<Tabs>`, `<Accordion>`) collapse to invalid GitBook tags
- AI-prompt blocks (`<Prompt>`, `<RequestExample>`) unroll inline and dominate the page
- API parameter tables strip the parameter names in AI-friendly exports
- Cover images, embedded videos, code-group package-manager labels — all routinely lost
- Internal links pointing to `/docs/...` paths that no longer exist after the move
If the export is missing material content, **flag this to the user immediately** and offer to either fall back to scraping the rendered HTML, or work from the original source repo (`.mdx` files) instead of the export. Don't silently produce a half-content site.
## Source-platform mappings
### Mintlify (`.mdx`)
The most common migration source. Mintlify uses `.mdx` with React components; GitBook uses extended markdown with custom block syntax.
| Mintlify component | GitBook equivalent |
|---|---|
| `<Card title="..." icon="...">` | A row in a `<table data-view="cards">` |
| `<CardGroup cols={3}>` | The full `<table data-view="cards">` |
| `<Tabs>` / `<Tab title="...">` | `{% tabs %}` / `{% tab title="..." %}` |
| `<Accordion title="...">` | `<details><summary>...</summary>...</details>` |
| `<Note>` | `{% hint style="info" %}` |
| `<Warning>` | `{% hint style="warning" %}` |
| `<Tip>` | `{% hint style="success" %}` |
| `<Info>` | `{% hint style="info" %}` |
| `<Steps>` / `<Step>` | `{% stepper %}` / `{% step %}` |
| `<CodeGroup>` with `\`\`\`sh npm` fences | `{% tabs %}` blocks — **the package-manager label after the language is the tab title**; converters that strip it lose the structure |
| `<Frame>` | Just an image; the frame chrome doesn't carry over |
| `<Prompt>` | `<details><summary>AI Prompt</summary>...</details>` — otherwise the prompt content unrolls inline and dominates the page |
| `<RequestExample>` / `<ResponseExample>` | A code block, optionally inside a tab |
| `<ParamField name="..." type="..." required>` | A row in an API params table — **note: in `llms-full.txt` exports, `name=` is often stripped; you'll need a re-pass from the original `.mdx`** |
| `<ResendParamField>`, `<YouTube />`, other custom components | Need case-by-case handling — there's no GitBook equivalent for a third-party-author custom component, so either inline a sensible substitute or note in a hint that the original had embedded content |
| `icon={<svg>...</svg>}` (inline SVG in a prop) | Convert the SVG into either a Font Awesome icon name (where one exists) or a separate file under `.gitbook/assets/` referenced via `<img>` — see write-docs for SVG handling |
| API reference pages (`api-reference/<endpoint>.mdx`) | **Don't convert these one by one.** Use `builtin:openapi` SUMMARY entries instead — see `block-ecosystem.md`. Mintlify API ref pages are auto-generated from the OpenAPI spec already, so the spec is the source of truth, not the `.mdx` files. |
### GitBook v1 (legacy)
Old GitBook sites export to a similar markdown shape but with different conventions:
- `SUMMARY.md` is at the repo root (not per-space) for single-book sites
- Cover images and rich-block syntax pre-date the current GitBook block ecosystem
- Internal links may use `path/to/page.md` directly (rather than `https://app.gitbook.com/s/...`)
For a v1→current migration, plan to lift content into the new monorepo layout (one folder per space), regenerate per-space `SUMMARY.md` files, and rewrite cross-space links to the resolved-ID pattern.
### Docusaurus
Docusaurus uses `.md` and `.mdx` with sidebars defined in JSON. The conversion is mostly straightforward:
- Sidebar JSON → `SUMMARY.md` (with `## Group name` headings derived from the JSON `category` entries)
- `:::note` / `:::warning` admonitions → `{% hint style="info|warning" %}`
- `<Tabs>` / `<TabItem>` (from `@theme/Tabs`) → `{% tabs %}` / `{% tab %}`
- Frontmatter mostly carries over directly, modulo some Docusaurus-specific keys (`sidebar_label`, `sidebar_position`) that map to SUMMARY ordering
- MDX with React components → case-by-case (most Docusaurus sites use these sparingly)
### ReadTheDocs / Sphinx
reStructuredText → markdown conversion (via `pandoc` or `rst2md`) is lossy. For RTD migrations:
- Run pandoc on each `.rst` to get markdown
- Walk the output for unconverted directives (`.. note::`, `.. code-block::`, `.. toctree::`) and convert them by hand
- The `toctree` is the IA spec — it maps to `SUMMARY.md`
### Generic HTML scrape
When all you have is a live site, scrape the rendered HTML, extract the article body (skip the chrome), and convert to markdown. This is the lowest-fidelity source — expect to do significant rework.
## Anchor-pages strategy
Migrating 200+ pages doesn't mean hand-crafting 200+ pages. The right approach is two-track:
1. **Bulk-convert the long tail** — `.mdx` → `.md` with mechanical component-to-block conversion. Acceptable level of polish: it renders, links work, hints are hints, code blocks are code blocks. Don't try to make every page beautiful.
2. **Hand-craft 4–6 anchor pages** — the homepage, the top-level landing of each space, the marquee how-to, the most-visited concept. These get the full GitBook treatment: card-tables for navigation, Mermaid diagrams instead of ASCII, Updates blocks for changelogs, OpenAPI auto-gen for API references, the right `width` and frontmatter for each.
The anchors set the standard for what the site can look like; the long tail gets brought up to that standard iteratively as the user touches each page in the future. Trying to anchor-quality every page in a single migration is how migrations stall.
When picking anchors, prefer:
- The site homepage (everyone sees it, it sets the visual tone)
- One landing per space (sets the per-space tone)
- The pages with the highest traffic in the source (where polish has the most impact)
## The format-pass
After bulk conversion, run a deliberate clean-up pass *before the first commit*. Naive conversion produces several classes of artifact that don't render well:
### Foreign component residue
Run a grep across the converted markdown for:
```bash
# Unconverted Mintlify components
grep -rn '<Card\|<CardGroup\|<Tabs\|<Tab\b\|<Note\|<Warning\|<Tip\|<Steps\|<Step\b\|<Accordion\|<Frame\|<Prompt\|<ParamField\|<ResendParamField\|<RequestExample\|<ResponseExample\|<YouTube' --include='*.md'
```
Each hit is either a missed mapping (apply the right GitBook block) or a custom component with no equivalent (leave a `<!-- TODO: original used <Foo> -->` marker and a hint asking the user to fill in the gap).
### Code fence metadata
Mintlify supports `\`\`\`bash {3-5}` (highlighted lines), `\`\`\`bash filename="install.sh"`, and other metadata after the fence language. GitBook ignores these and sometimes parses them as part of the language identifier. Strip everything after the language token unless you know the GitBook equivalent:
```bash
# Find code fences with extra metadata after the language
grep -rn '^```[a-z]\+ [^a-z]' --include='*.md'
```
### Broken internal links
Source-platform paths (`/docs/...`, `/getting-started/...`) usually don't survive the migration. After the GitBook structure is known, walk every markdown file and rewrite:
- Within-space links: relative `.md` paths (e.g. `[Authentication](concepts/authentication.md)`)
- Cross-space links: `https://app.gitbook.com/s/<spaceId>/<path>` (use `XSPACE_<KEY>` sentinels during scaffolding — see `cross-space-links.md`)
- External links: leave alone
The script for this is small but specific enough that it's worth writing per-migration:
```python
import re
from pathlib import Path
# Map of old paths (from the source platform) → new GitBook paths
LINK_MAP = {
"/docs/getting-started/quickstart": "getting-started/quickstart.md",
"/docs/api/authentication": "https://app.gitbook.com/s/XSPACE_API/authentication",
# ... one entry per old path
}
for md in Path(".").rglob("*.md"):
text = md.read_text()
new = text
for old, new_path in LINK_MAP.items():
new = re.sub(rf']\({re.escape(old)}([#?][^)]*)?\)', f']({new_path}\\1)', new)
if new != text:
md.write_text(new)
```
Build the LINK_MAP from the source platform's sitemap or sidebar JSON.
### YAML frontmatter quoting
Source titles with `:`, `#`, or other YAML-significant characters break Git Sync silently when copied verbatim into frontmatter. The format-pass should walk every page's frontmatter and quote `title:` and `description:` values that contain these characters. Cheap to do, expensive to debug if missed.
### Auto-generated icons and descriptions
The two most common bad-default migration patterns:
- **`icon: <something inferred from the URL slug>`** — produces a sea of mismatched cog/info/file icons that look wrong against an originally icon-less source. **If the source had no icons, leave the icon field blank.** Only carry icons forward when the source explicitly had them.
- **`description: "Source: https://old.example.com/page"`** — that string leaks into sidebar previews, search results, and social shares. **If you don't have a real description, leave the field out.** A blank description is much better than a stub one.
These are easy to enforce in the converter: only emit `icon:` and `description:` when the source frontmatter had them; never synthesize.
## Idempotency and homepage hazards
If you're using a custom converter or SUMMARY-generator that runs in steps, make every step idempotent by default:
- **Skip files that already exist.** A second run of the converter that clobbers a hand-edited homepage is the most common painful surprise.
- **Don't auto-promote `<space>/introduction.md` to `<space>/README.md` unconditionally.** Guard with `if not README.exists()`. Otherwise the user's hand-tuned README gets overwritten on every regenerate.
- **Never `rm -rf <space>/` to "regenerate cleanly".** If the converter genuinely needs to start from a clean slate, write into `<space>/_generated/` and let the user merge or diff into the live tree manually. Hand-edited content lives in the live tree; generated content lives next to it.
The general rule: **converters are append-only by default, destructive only on explicit opt-in.**
## Sequencing
A clean migration runs in this order:
1. Pre-flight: fetch source, catalog IA, sample-check the export
2. Plan structure with the user (sections, groups, page lists per space — agreed before any file is written)
3. Bulk-convert the long tail with the format-pass applied
4. Hand-craft the anchor pages
5. Internal-link sweep with the LINK_MAP
6. Resolve cross-space link sentinels (after spaces are created via the GitBook API)
7. Initial commit, push, Git Sync handoff
If you find yourself doing 4 before 3 (anchor pages while bulk content is still missing), or 5 before 2 (link sweeping before the structure is agreed), the migration is going to thrash. The order matters.references/site-structure-design.md
# Site structure design
A solid information architecture is the difference between docs people use and docs they bounce from. This file captures the heuristics for going from raw inputs to a structure plan that you'll show the user before scaffolding any files.
## The output of this step
By the end, you should have three things, all small and reviewable:
1. **The space list** — usually 1 to 4 spaces. Each space gets a one-sentence description and an emoji.
2. **The section grouping**, only if multi-space — sections are top-nav buckets that hold spaces.
3. **The page tree per space** — folders and pages. Each page gets a 1-line description.
That plan goes to the user before any files get written. Restructuring later is fine inside Git but expensive after publish.
## Heuristics for picking spaces
A space is a heavyweight unit: its own URL slug, its own Git Sync, its own customization overrides, its own visibility settings, and its own search scope. **Don't split content across spaces unless the audiences or lifecycles really differ.**
Useful prompts to decide:
- **Different audiences** — end users vs. developers vs. ops staff usually want their own space.
- **Different lifecycles** — a changelog updates daily; user docs update with releases; an architecture overview updates rarely. These benefit from being separate.
- **Different review processes** — if API reference is auto-generated from OpenAPI but user docs are hand-edited, splitting them makes the auto-update story cleaner.
- **Different visibility** — internal runbooks alongside public docs probably want separate spaces (and possibly separate sites).
When in doubt, **start with fewer spaces.** Splitting later is cheaper than merging.
### Common multi-space shapes for SaaS / API products
- **Two-space**: `Guides` (everything narrative) + `API Reference` (endpoint-by-endpoint). The simplest case.
- **Three-space**: `Guides` + `API Reference` + `Changelog`. Add this when release notes are frequent and substantial.
- **Four-space**: `Guides` + `API Reference` + `Changelog` + `SDKs`. Add SDKs as its own space when there are 3+ language bindings each with their own narrative.
- **Plus a separate internal space**: when there's an internal handbook or runbook content that shouldn't live alongside public docs, that's a different *site* (or a private space added to the same site with restricted visibility), not just another section.
## Heuristics for sections
Sections are top-level navigation groupings *within* a site. They show up as the top-nav row above each space's sidebar. Use sections when you have 3+ spaces and want them grouped — for example, "Product" (Guides + Tutorials) and "Developers" (API + SDKs).
If the site has only 1 or 2 spaces, skip sections — they add UI clutter without clarifying anything.
A section with a single space is fine as long as the section title clarifies the audience or topic in a way the space title alone wouldn't.
## Designing the page tree per space
The default rule of thumb: **shallow is better than deep.** A two-level tree (groups → pages) handles most spaces. Three levels is the upper limit before navigation gets disorienting.
### Patterns that tend to work
For a guides space:
```
README.md (overview, value prop, "what you can do here")
getting-started/
installation.md
quickstart.md
first-project.md
core-concepts/
<one page per concept, named with the noun>
how-to/
<one page per task, named "Verb the Object">
reference/
cli.md
configuration.md
troubleshooting.md
```
For an API reference space (when not using OpenAPI auto-generation):
```
README.md (auth overview, base URL, conventions)
authentication.md
errors.md
rate-limits.md
endpoints/
<one page per resource, with all its methods on the same page>
webhooks/
<one page per webhook event>
```
For a changelog space:
```
README.md (subscribe links, deprecation policy)
2026/
2026-04.md
2026-03.md
...
2025/
...
```
### Anti-patterns to avoid
- **Mirroring an org chart.** Users don't care which team owns what; group by topic, not by reporting line.
- **One page per tiny topic.** If a page is going to be three sentences, fold it into a longer page.
- **Deep nesting "for organization".** Three folders deep usually means the second-level grouping is wrong.
- **Inconsistent naming.** Pick "verb the noun" or "Noun" for page titles and stick with it within a space.
## Going from raw inputs to a plan
### When the user has a folder of existing markdown
Start by listing all the files and reading the first ~100 lines of each. Look for:
- **Filenames as a signal** — `installation.md`, `auth.md`, `how-to-deploy.md` already suggest groupings.
- **Cross-references** — files that link to each other heavily belong in the same space.
- **Frontmatter** — if there's existing frontmatter with categories or tags, use it.
- **README files** — existing README.md or index.md files reveal the user's mental model.
Propose a structure that respects existing groupings where they're sensible, but call out reorganizations explicitly: "I'm proposing to move `legacy-api.md` from the top level into a `reference/` folder — does that work?"
### When the user has examples and content sketches
Read the examples (especially competitor docs the user pointed to) for structural ideas, but don't copy them blindly. Ask:
- What's the smallest set of pages that would make a v1 of this site useful?
- Which 3-5 questions will most readers arrive with? Make sure those have obvious entry points.
- What's the user journey from "first time visitor" to "power user"? The structure should support that journey.
A good v1 is usually 8–20 pages across 1–2 spaces. Don't pad with placeholders — empty pages hurt more than missing pages.
### When the user gives only a description
Ask one focused clarifying question to nail down audience and scope, then propose a minimal structure. Example questions:
- "Is this for end users of the product, developers integrating with it, or both?"
- "Should the docs site be public, or behind authentication?"
- "Are there any sections you absolutely need on day one — pricing, FAQ, status?"
Then propose, e.g., a single-space site with `README.md`, `getting-started/`, `how-to/`, and `reference/` — and iterate from there.
## Worked examples
### Example 1 — Open-source CLI tool, single contributor
**Inputs:** GitHub repo, README, CHANGELOG.md, a few notes in `docs/`.
**Plan:**
- Single-space site, name "Acme CLI Docs"
- Tree:
```
README.md (overview + install one-liner)
installation.md
commands/
<one page per top-level command>
configuration.md
troubleshooting.md
changelog.md (linked from README.md, kept up to date manually)
```
- Branding: clean theme, primary color from the project's logo, GitHub footer link.
### Example 2 — Series-A SaaS with a public API
**Inputs:** Marketing site, internal Notion with scattered guides, an OpenAPI spec, a brand guide.
**Plan:**
- Three-space site, sections: "Product" (Guides), "Developers" (API Reference, Changelog)
- Spaces:
- `guides/` — onboarding, how-tos, concepts, integrations
- `api-reference/` — generated from the OpenAPI spec; pages organized by resource
- `changelog/` — quarterly, with date-stamped entries
- Branding: brand color, custom logo, Inter font, footer with company links and a "Status" link
### Example 3 — Restructure an existing site
**Inputs:** An existing GitBook site with one over-stuffed space ("Documentation"), 80+ pages.
**Plan:**
- Don't reorganize without reading the current structure first (`GET .../structure`)
- Propose splitting into `Guides` + `API Reference`, with the API content moving wholesale
- Identify which pages are heavily linked and minimize their path changes (or set up redirects in `.gitbook.yaml`)
- Show the proposed before/after diff to the user before any moves
## Sign-off
After producing the plan, ask the user something like:
> Here's the proposed structure. Anything you'd change before I scaffold the repo and create the spaces? Specifically: (1) does the space split feel right, (2) are there any pages I've missed or grouped wrong, (3) any naming you'd tweak?
Wait for explicit confirmation. Then proceed.SKILL.md
---
name: configure-site
metadata:
version: "1.0"
description: "Create and maintain entire GitBook documentation sites end-to-end — design the site structure from source content, scaffold a Git repository in monorepo layout, set up the GitHub/GitLab remote, drive the GitBook API (via its REST API or MCP server) to create the site/sections/spaces, apply branded customization, and hand the user clean instructions for the one UI step (Git Sync wiring) that GitBook does not expose programmatically. Always set up Git Sync at the site level first — mapping every space to a directory in one repo/branch via gitbook-docs.yaml — and only fall back to per-space Git Sync when one space genuinely needs an independent repo or branch. Trigger this skill whenever the user wants to spin up a new GitBook docs site, restructure or extend an existing one, link a site or spaces to a Git repo for sync, change a site's branding (logo, colors, fonts, header/footer), or programmatically manage spaces, sections, or site-spaces. This skill is the orchestration layer; for authoring the markdown content of any individual page it defers to the companion `write-docs` skill."
---
# Configure GitBook Site
A skill for creating and maintaining entire GitBook documentation sites. Where `write-docs` covers what goes inside a single page, this skill covers everything around the pages: structure design, repo scaffolding, the GitBook API, and branding. Use the two skills together — this one calls into `write-docs` whenever it needs to generate or edit page content.
## How you can talk to GitBook
There's more than one way to drive GitBook — GitBook's MCP server and the REST API. Check what's actually available in the current session and prefer **MCP first**: if GitBook MCP tools are already connected, use them for anything they cover (creating/configuring sites, opening change requests, drafting and editing content, restructuring docs) instead of making direct API calls. Don't run a detection script for this — you already know your own available tools/MCP connections; just use that awareness.
**"MCP first" is about transport, not about bypassing Git Sync for content.** MCP exposes a change-request content-push tool (`updateChangeRequestContent`) that's tempting to reach for anytime it's connected — but for spaces that already have Git Sync configured, pushing content by editing files in the local repo and letting Git Sync carry it to GitBook is still the preferred path for anything beyond a small, targeted edit. Use the change-request push (MCP or REST) instead when the space isn't Git-synced, there's no local checkout available in the environment, or the edit is small enough that opening a CR is proportionate. See `write-docs`'s "Choosing Git Sync vs. a change-request content push" for the full rule — it applies here too.
The steps in this skill are described as outcomes ("list the orgs", "create the site", "add a section") rather than tied to one transport, so they apply whichever you use. If GitBook MCP tools are connected, call those directly — their own schemas describe their parameters. If you're on the REST API path instead, the exact endpoints, request bodies, and expected responses for each step are in `references/api-cheatsheet.md`.
- **GitBook MCP** — a full read/write surface over the same capabilities described below, not a narrower view. If it isn't connected yet and the task is substantial enough to benefit (a full site build, ongoing restructuring — not a one-off tweak), offer to set it up: `claude mcp add --transport http gitbook-mcp https://mcp.gitbook.com/mcp` (then `/mcp` to complete OAuth sign-in — or append `--header "Authorization: Bearer $GITBOOK_TOKEN"` to skip the browser flow). Codex equivalent: `codex mcp add gitbook-mcp --url https://mcp.gitbook.com/mcp`. Note: this is a different server from GitBook's separate, read-only "published docs" MCP, which only exposes already-published content.
- **REST API** (`https://api.gitbook.com/v1`) — the fallback when MCP isn't connected, or for anything MCP doesn't cover. Needs `GITBOOK_TOKEN` as a bearer header on every request.
The same personal access token (from https://app.gitbook.com/account/developer) works as the bearer token for both. MCP additionally supports OAuth as a friendlier alternative to pasting a token.
**If you end up needing a token** (REST API path, or MCP without OAuth), check for it at the start of the session:
```bash
[ -n "$GITBOOK_TOKEN" ] && echo "Token found" || echo "GITBOOK_TOKEN is not set"
```
If `GITBOOK_TOKEN` is not set, ask the user directly:
1. Tell them they need a GitBook personal access token. Direct them to **https://app.gitbook.com/account/developer** to create one.
2. Ask them to paste the token into the conversation. Immediately export it as an environment variable (`export GITBOOK_TOKEN=<pasted value>`) and don't repeat it back in your response.
3. Do not proceed with any API calls until the token is confirmed present in the environment.
Never write the token to a file, never echo it back in a response, never commit it.
## The fundamental constraint
The most important thing to internalize before doing anything: **GitBook can do almost everything except set up Git Sync, regardless of transport**. Authorizing GitHub/GitLab, picking the repository, choosing the branch, and choosing the initial sync direction are all UI-only operations — both the REST API and MCP (which wraps it) only let you *read* the resulting Git Sync state, never set it up. There's an API operation, `installGitSyncProviderOnTarget`, that targets either a site or a space, but the account-connection (OAuth) step still has to happen in the app, and it isn't yet exposed through GitBook's MCP server — treat it as not-yet-usable rather than building a flow around it.
**Git Sync now configures at the site level, and that's the default to reach for.** One connection (one repo, one branch) covers the whole site; `gitbook-docs.yaml` maps each space to its own directory, which is the same shape this skill already scaffolds a monorepo into. Per-space Git Sync still exists, but it's now the exception — reach for it only when a specific space needs an independent repo or branch (e.g. a private space that can't live in the public docs repo).
That means the cleanest end-to-end flow is always:
1. Claude scaffolds a Git repo locally as a monorepo (one directory per space), ideally with `gitbook-docs.yaml` pre-authored mapping each space to its directory, and pushes the remote when tooling permits
2. Claude creates the site, sections, and any empty spaces it can
3. **The user does one short, well-scripted UI step in GitBook: connect the site to the repo/branch and confirm the space-to-directory mapping** — not one step per space
4. Claude applies branding/customization
The user's role in step 3 is unavoidable but should never be a surprise — generate clear, copy-paste-ready instructions for them. Reference: `references/git-sync-handoff.md`.
If the user explicitly does not want Git Sync, fall back to the content-import path (content import and template application) — covered briefly below and in `references/api-cheatsheet.md`.
## Inputs you should gather up front
Don't start scaffolding until these are known. If something is missing, ask once with a focused question rather than guessing. (Auth is handled separately — see "How you can talk to GitBook" above.)
- **Organization** — list the user's orgs and **show the list to the user, then ask them to confirm which one is the target by name**. Do this even if they have only one org — confirming once up front is cheap insurance against creating sites in the wrong place. Save the chosen `organizationId` for the rest of the session and refer to the org by its title (not its UUID) when narrating subsequent steps.
- **Site plan and visibility** — **default to `type: site` on the Ultimate plan**, public visibility, unless the user explicitly says otherwise. Most real customers want the Ultimate feature set (custom domain, AI Assistant, advanced customization, hidden GitBook trademark, custom fonts, custom logos). The free tier (`type: basic`) is appropriate only for clearly low-stakes use cases like solo open-source side projects. If you're unsure, ask: *"I'll set this up on the Ultimate plan unless you'd prefer the free tier — should I downgrade?"* — Ultimate features that are silently absent on `basic` (no AI assistant, no custom fonts, no custom domain) are a much bigger user surprise than briefly confirming the plan.
- **The content seed** — what's the site being built from? Common shapes:
- A folder of existing markdown — the cleanest starting point
- A handful of notes plus a competitor's site as a reference
- Just a description of what they want to document
- An existing site they want to restructure (in which case fetch the site's current structure first)
- **A migration** from another docs platform (Mintlify, Docusaurus, ReadTheDocs, GitBook v1) — see `references/migration-from-other-platforms.md` for the workflow. Migration is its own discipline; don't treat it as a glorified file copy.
- **OpenAPI spec for the API reference** — if the site has any API reference content, **ask up front whether they have an OpenAPI spec** (or whether one can be generated from their codebase). If yes, the API reference space is one `builtin:openapi` SUMMARY entry plus a one-paragraph overview README per resource — dramatically less work than hand-authored endpoint pages, and never drifts. See `references/block-ecosystem.md` and `references/api-cheatsheet.md` for the workflow. **Don't default to hand-authored endpoint pages** — they're almost always the wrong call.
- **Branding** — at minimum, primary color (hex). Optionally: logo URLs (light + dark), favicon, font choice (or one of GitBook's defaults), header links, footer text/links, theme preset (`clean`, `muted`, `bold`, `gradient`). For Ultimate sites, also consider AI-assistant starter prompts (3-5 short questions visitors are likely to ask).
- **Site structure** — sections, not site-spaces. If the site has more than one space, plan the **section list** with the user explicitly: each section has a title, a Font Awesome icon name, and a description. Section icons and descriptions are first-class navigation furniture — visitors see them — and gathering them up front saves a follow-up update per section later. Example: `[{title: "Guides", icon: "book-open", description: "Concepts and tutorials"}, {title: "API Reference", icon: "code", description: "REST API and SDKs"}, {title: "Changelog", icon: "clock-rotate-left", description: "Updates and release notes"}]`.
- **Git remote preference** — GitHub, GitLab, or local-only. Check whether `gh` or `glab` are installed *before* asking. If neither tool is available, **say so explicitly** and offer two paths: (1) commit locally and put the "create the remote and push" step at the top of the user's handoff, or (2) ask the user to install the tool. Don't quietly default to local-only without telling them — they'll have a repo with no remote and no instructions.
- **Site shape** — single space or multi-space. Multi-space sites use **sections** to group spaces in the navigation; this is the right choice when content has clearly distinct audiences (e.g. user docs + API reference + changelog). Use site-spaces directly only for translation variants — see `references/api-cheatsheet.md`.
## Verify the content source before building
Once the user names a content seed — a repo, folder, or docs-site URL — verify you can actually read it **before** designing structure or scaffolding anything:
1. **Resolve and echo the source.** State exactly what you're about to read (repo URL and branch, folder path, or site URL) and show the user its top-level contents — a short file or page list — so they can confirm it's the right one.
2. **If you can't access it, stop and say so.** Git hosts return **404 for private repositories** — indistinguishable from "repository doesn't exist." Treat any 404 or clone failure on a user-named repo as *possibly private*: tell the user what failed, and ask them to either make the content reachable (local clone, archive, authenticated `gh`/`glab`, public mirror) or correct the URL. Check whether an authenticated `gh`/`glab` CLI is available before declaring the repo unreachable.
3. **Never substitute a source.** Do not search for, guess, or fall back to a similarly-named repository or site — even one that looks identical. Building a docs site from the wrong source is far worse than pausing to ask. Any change of source requires the user's explicit sign-off.
## Confirmation gates for state-changing operations
Site creation, space creation, adding sections, attaching site-spaces, and customization changes all create or modify objects that are **immediately visible to everyone in the org** and that take real effort to clean up. Treat them as heavy operations.
The rule: **never make a state-changing change without first showing the user a one-screen preview of exactly what's about to happen and getting an explicit "yes".**
A good preview is short and concrete:
> About to run, in org **Acme Inc** (`org_abc123`):
> - Create site **"Acme Platform Docs"** (type: site, plan: ultimate, visibility: public)
> - Create 3 empty spaces: **Guides**, **API Reference**, **Changelog**
> - Add Guides as the default section; create sections for API Reference and Changelog
>
> Proceed? (yes/no)
Bad previews are vague ("I'll create the site now") or buried in a wall of explanation. Keep it scannable.
The same rule applies to destructive operations — deleting a site, space, section, or customization override — only with even less ambiguity ("This will delete site **Acme Platform Docs** along with its 3 spaces. Spaces and sites are recoverable for 7 days, then permanent. Confirm?").
When the user has already confirmed a multi-step plan in the structure-design step, you don't need to ask again for each individual operation inside that plan — but if anything in the plan changes (an extra space, a different visibility), re-confirm.
For read-only operations (fetching or listing), no confirmation is needed.
## After a change-request push: two links are mandatory
Whenever this skill (or `write-docs`, which it delegates page authoring to) pushes content through a change request — via MCP's `updateChangeRequestContent`/`create_change_request`/`submit_or_merge_change_request` curated tools, `invoke_operation`, or the REST equivalents — the edit is **not finished** until both of the following have been reported back to the user, every time:
1. **The change request's diff/editor link** (`urls.app`) — the link to review the change in the GitBook app.
2. **The site preview link** — the rendered docs with the change applied. This takes a separate lookup: the site URL lives on the **Site** object (`urls.published` when the site is public, else `urls.preview`), not the change-request object, and **you must append `/~/changes/<number>/` to it**, stripping the trailing slash the API returns. Without that segment the link renders the site's *current* content rather than this change request — it loads fine and shows the wrong thing.
This is a hard rule, on the same footing as the confirmation gates above — not a nicety to add if there's time. See `write-docs`'s "Two links are mandatory whenever a change request is involved" and the `cr-create` skill's "Surfacing the preview link" for the exact resolution steps (MCP: `getSpaceById` → find the site via `list_sites`/`get_site_structure` or each site's site-spaces → `getSiteById` for `.urls.preview`; REST: the equivalent chained `GET` calls). If the space isn't attached to a published site, say so plainly rather than only giving the diff link with no explanation.
## Designing the site structure
Before writing any files or creating anything in GitBook, decide on the structure and run it past the user. A weak structure is the single biggest reason docs sites fail to land.
The output of this step is a small plan, ideally three pieces:
1. **The space list** — one space per coherent body of content. Keep it small (1–4 spaces is typical). A space is a unit of navigation and Git Sync, so don't split a single audience's content across spaces.
2. **The section grouping** (if multi-space) — sections are top-level partitions in the site nav, e.g. "Product" / "Developers" / "Resources". A section can hold one or more spaces.
3. **The page tree per space** — folders and pages, with one or two sentence summaries of each page. The depth should match the content; shallow trees (1–2 levels) are usually best.
The full set of heuristics for going from raw inputs to a structure plan is in `references/site-structure-design.md` — read it the first time you do this for a non-trivial site. **Always show the plan to the user and get explicit sign-off before scaffolding files.** Restructuring later is cheap inside Git but expensive once a site is published and indexed.
A note on confirmation when the user gives one collapsed instruction: prompts like *"plan the structure and then scaffold it"* tempt you to skip the gate. Don't. Present the plan as a clear, scannable block, then either wait for a "yes" or — if you've already started scaffolding because the prompt was that explicit — surface what you decided in the plan and offer one easy chance to redirect ("if any of this is off, tell me and I'll redo before going further"). The point is that the user sees the plan **before** they're staring at twenty generated files, while a redo is still cheap.
## Scaffolding the repository
Once the structure is agreed, lay out the repo as a monorepo — even for single-space sites, this is consistent and future-proof. Each space is a directory containing its own `README.md` (homepage) and `SUMMARY.md` (table of contents). Optionally a `.gitbook/` folder for per-space variables and reusable content blocks, and optionally a `.gitbook.yaml` for advanced sync configuration.
Example layout for a three-space site:
```
my-docs/
├── .gitignore
├── README.md # repo-level readme (not a space homepage)
├── guides/ # space 1
│ ├── README.md # space homepage
│ ├── SUMMARY.md
│ ├── .gitbook/
│ │ └── vars.yaml # optional: space-level variables
│ ├── getting-started/
│ │ ├── installation.md
│ │ └── quickstart.md
│ └── concepts/
│ └── ...
├── api-reference/ # space 2
│ ├── README.md
│ ├── SUMMARY.md
│ └── endpoints/
│ └── ...
└── changelog/ # space 3
├── README.md
└── SUMMARY.md
```
A few notes about this layout that often trip people up:
- **`.gitbook.yaml` is optional.** GitBook works fine on the default convention of `README.md` + `SUMMARY.md` per space. Only add a `.gitbook.yaml` when you need to override the root, define redirects, or do something else non-default. The bundled example site (`references/example-site/`) has zero `.gitbook.yaml` files and works perfectly.
- **`.gitbook/vars.yaml`** holds space-scoped variables that pages can reference inline (e.g. `support_email: support@evolve.com` referenced as `{% vars.support_email %}`). Useful for any value that appears on many pages.
- **`.gitbook/includes/<name>.md`** holds reusable content blocks — a snippet you embed in many pages with `{% include "...persona-switcher" %}`. Use these instead of copy-pasting boilerplate.
- The space directory name (e.g. `guides/`) is what the user maps that space to under **Content mapping** when wiring up site-wide Git Sync — not the site's "Project directory" field, which only points at where `gitbook-docs.yaml` itself lives (the repo root, in this layout). Don't conflate the two; see `references/git-sync-handoff.md`.
- Consider pre-authoring a `gitbook-docs.yaml` at the repo root that maps every space to its directory (see `references/git-sync-handoff.md` for the shape). GitBook reads it on first sync, so the user has less to fill in by hand during setup.
A minimal `.gitbook.yaml`, when you do need one, looks like:
```yaml
root: ./
structure:
readme: README.md
summary: SUMMARY.md
```
### The repo-level README and .gitignore
The repo-level `README.md` (top of the repo, not inside a space) should explain what the folder is and how it relates to the published site — not duplicate the docs themselves. A short paragraph is enough:
```markdown
# my-docs
Source for the [My Product docs site](https://docs.example.com). Each top-level folder
is a separate GitBook space; edits flow in both directions via Git Sync once configured.
```
A `.gitignore` should keep OS junk and editor settings out of the repo. Reasonable default:
```
.DS_Store
Thumbs.db
*.swp
*.swo
.idea/
.vscode/
```
If the team has additional generated artifacts (e.g. an OpenAPI spec built from source elsewhere), add those.
### Generating SUMMARY.md — gather the nav, don't infer it from folders
The most common scaffolding mistake is to walk the file tree and emit a SUMMARY.md from it: README at the top, every other file as a child indented under the README. This produces dispiriting nav — every page is a "child of the homepage", folder names become group titles whether or not they're meaningful to a reader, and the IA mirrors the file system instead of the user's mental model.
**The right pattern, in order:**
1. **Gather the desired navigation from the user during structure design.** Ask them — explicitly — to enumerate the top-level pages and the named groups for each space. This is the place where you make folder names match nav reality, and where the user can tell you "actually I want Authentication as a top-level page, not under Concepts."
2. **Lay out folders to match the agreed nav, not the other way around.** If the user wants three groups in a space — "Getting started", "Concepts", "Tutorials" — the space directory has three subfolders by those names (slugified), each with its own pages. Don't auto-extract a fourth group from a stray subfolder.
3. **Write SUMMARY.md to the explicit shape the user agreed.** The grammar that GitBook honors:
```markdown
# Table of contents
* [Space homepage](README.md)
* [Top-level page A](top-level-a.md)
* [Top-level page B](top-level-b.md)
## First group
* [Page in group](first-group/page.md)
* [Another page](first-group/another.md)
## Second group
* [Page](second-group/page.md)
```
Key shape rules:
- **README.md is on its own line at the very top, as a sibling**, not a parent. Other top-level pages follow as siblings.
- **`## Group name` headings introduce groups.** Pages in a group are flat bullets directly under the heading — *not* indented under the README.
- **Avoid mechanical "* README.md" + nested-everything-under-it.** That collapses the entire nav into one tree under the homepage and makes every page look like a sub-page of the homepage in the sidebar.
- **Group names come from the user, not the folder names.** "concepts/" can be the folder slug, but the group heading might be "How it works" if that's clearer.
- **One bullet per page, no extra formatting.** No bold, no descriptions in the SUMMARY — those live in the page frontmatter.
4. **Special-case patterns** that aren't plain bullets:
- **OpenAPI auto-generated endpoint pages** use a fenced YAML block as the bullet content (`type: builtin:openapi` — see `references/api-cheatsheet.md`).
- **External links** are `* [Title](https://...)` and render as outbound links in the nav.
- **Cross-space links** in SUMMARY.md use the same `https://app.gitbook.com/s/<spaceId>/<path>` form as in body content. Path has no `.md` suffix. During scaffolding write the sentinel form (`XSPACE_<KEY>`); resolve after space creation. See `references/cross-space-links.md`.
If your scaffolding helper auto-generates a SUMMARY.md by walking folders, **make it idempotent and skip files that already exist**. A user-edited SUMMARY.md should never be silently clobbered — that's how hand-tuned navigation gets lost.
### Per-page markdown — defer to write-docs, but reach for the rich blocks
**For all markdown files** — `README.md`, `SUMMARY.md`, every page — follow the `write-docs` skill. It is the authoritative reference for:
- **Frontmatter** including the `icon:` field. Icons are Font Awesome names without the `fa-` prefix (e.g. `book-open`, `bolt`, `house`, `code`, `puzzle-piece`, `id-card`, `circle-dollar-to-slot`). Don't invent names — pick from the Font Awesome catalogue. The example site uses these in nearly every page's frontmatter.
- **Layout flags** including `layout: width: wide` (use selectively — on marketing-style landing pages, on changelog pages with the Updates timeline, on pages with multi-column blocks or genuinely wide tables. **Don't default to wide for every space homepage** — the GitBook default width is right for documentation, including documentation landing pages with card-tables. Wide is for hero-style marketing layouts, not normal docs.), `cover:` images, and per-page visibility flags (`title.visible`, `tableOfContents.visible`, etc.).
- **`SUMMARY.md` grammar.** Strict format — one bullet per page, optional `## Group name` headings, no extra formatting. Plus the special `type: builtin:openapi` syntax for auto-generating endpoint pages from a spec.
- **Rich blocks** — tabs, hints, steppers, columns, card-tables, expandables, embeds, conditional content with `{% if visitor.claims... %}`, the OpenAPI block, the **Updates** block (changelog), reusable content includes.
- **GitBook-flavoured markdown differences** from CommonMark.
Don't reinvent any of that here. The bundled `references/example-site/` is the best practical reference for what idiomatic content looks like.
### Choosing the right block — actively, not by default
A common failure mode: Claude generates docs that *work* but use plain markdown for everything, missing the rich blocks that make GitBook sites feel like a real product. **The skill should actively reach for specialized blocks**, not fall back to bare prose-and-bullets. Concrete patterns to internalize:
- **Changelogs** → `{% updates %}` block with `{% update date="..." tags="..." %}` entries. Auto-generates RSS, supports tags (defined in `.gitbook/tags.yaml`). Don't write `## YYYY-MM-DD` headings — that's the wrong shape.
- **API endpoint references** → OpenAPI spec uploaded once, auto-generated pages via `type: builtin:openapi` in SUMMARY.md. Don't hand-author endpoint pages — they drift, and the spec is canonical anyway. If the user doesn't have a spec, offer to draft a minimal one rather than going prose-y.
- **State machines, flows, sequences, simple architecture** → ` ```mermaid ` fenced blocks. Don't draw boxes-and-arrows in ASCII; Mermaid is supported, renders cleanly, and is screen-reader friendly.
- **Space homepages** → use the GitBook default layout for normal docs landings (TOC visible, default width). Reach for `layout: width: wide` only when the page is genuinely marketing-style — a hero image, an unusually large card grid, a multi-column dashboard layout. The default is right for docs.
- **"Choose your path" content** → card-tables (`<table data-view="cards">`). The HTML is verbose but the visual result beats any markdown alternative.
- **Side-by-side intro patterns** → `{% columns %}` block. Two columns at 50/50 is the standard.
- **Repeated boilerplate (3+ places)** → `.gitbook/includes/<name>.md` + `{% include "..." %}`.
- **Repeated literals (env URLs, support email, version pin)** → `.gitbook/vars.yaml` + `<code class="expression">space.vars.<name></code>`.
- **Multi-language code samples** → `{% tabs %}` block.
- **Walkthroughs of 3+ ordered steps** → `{% stepper %}` block.
The full block-by-block guide, with example invocations and a smell-vs-fix decision table, is in `references/block-ecosystem.md`. **Read it before generating any non-trivial page**, and walk the decision table for each content area being scaffolded — ask "is there a specialized block for this?" before defaulting to plain markdown.
### Cross-space links
Multi-space sites *want* cross-space links — they're how a site feels like one connected product, not a bunch of separate manuals. **Don't duplicate content to avoid them, and don't drop them.** They're a first-class GitBook feature; the only twist is that they need real space IDs to render correctly, and IDs only exist after the site is created.
The pattern in markdown is just a regular link to the GitBook URL of the target space:
```markdown
For the conceptual side, see the [Authentication concept page](https://app.gitbook.com/s/<spaceId>/concepts/authentication).
```
GitBook resolves `https://app.gitbook.com/s/<spaceId>/<path>` at render time, regardless of your custom domain. Internally these are `ContentRefPage` or `ContentRefSpace` content references with the space ID set; in markdown they just appear as URLs.
**The scaffolding flow:**
1. **During scaffolding**, write cross-space links using a sentinel space-ID prefixed with `XSPACE_`, one per planned space. Use the space slug from your structure plan as the suffix:
```markdown
See the [Authentication concept page](https://app.gitbook.com/s/XSPACE_GUIDES/concepts/authentication).
For the full reference, see the [API Reference](https://app.gitbook.com/s/XSPACE_API/).
```
These are valid markdown links to non-existent GitBook spaces — they don't break the parser, they're easy to grep for, and they round-trip cleanly through Git.
2. **After space creation**, once you have each new space's real ID, walk every markdown file and substitute `XSPACE_<KEY>` with the real space ID:
```bash
sed -i \
-e "s|XSPACE_GUIDES|${GUIDES_SPACE_ID}|g" \
-e "s|XSPACE_API|${API_SPACE_ID}|g" \
-e "s|XSPACE_CHANGELOG|${CHANGELOG_SPACE_ID}|g" \
$(find . -name '*.md' -not -path './.git/*')
```
3. **Commit and push** the resolution. GitBook will pick it up via Git Sync and the links will resolve on the next render.
For a clean implementation, keep a `cross-space-links.yaml` in the repo root mapping sentinel keys to space IDs, generated after creation. That makes the resolution script reproducible if anyone re-runs it. The full pattern, including anchor links, page-specific links, and a sample resolution script, is in `references/cross-space-links.md`.
**Where this gets written into:** the scaffold (with sentinels), the markdown content as you generate it across spaces, and the post-creation resolution step. Don't try to write real `app.gitbook.com/s/<id>/...` links during scaffolding — IDs don't exist yet, and any guess will be a broken link.
### Where to look for example content
`references/example-site/` is a **pruned snapshot** of a production-style GitBook site bundled with this skill. The original is a 12-space site (home, three product spaces, three versions of a developer API, three guides spaces, partners, changelog, plus an external-content `connections/` tree) with ~200 content files; the bundled snapshot keeps ~150 to stay within file-count limits.
**Read `references/example-site/PRUNE-NOTES.md` first** — it explains exactly what was kept and dropped, and lists the highest-signal files to read for specific patterns. The TL;DR:
- The full structural backbone of every space is intact — `README.md`, `SUMMARY.md`, `.gitbook/vars.yaml`, `.gitbook/includes/`.
- `developers/v2/` is kept in full as the canonical example. `developers/v1/` (legacy) and `developers/v3/` (beta) were dropped — they were structurally identical to v2 with version-specific content variations. The pattern of multi-version API docs is documented in PRUNE-NOTES.md and visible in `structure.json`.
- `connections/` — each subfolder (`blog/`, `community/`, `youtube/`) keeps its `index.html` plus one representative article so the metadata patterns stay learnable.
- `customization.json` and `structure.json` are the complete API exports describing the entire original site, including spaces and pages that were pruned. SUMMARY.md files inside each space also describe the original tree — some links in them point to pruned pages, which is expected.
Notable files to study, **organized by pattern you're trying to demonstrate**:
| Pattern | File to read |
|---|---|
| Updates block + tags | `changelog/README.md` + `changelog/.gitbook/tags.yaml` |
| `builtin:openapi` SUMMARY pattern | `developers/v2/SUMMARY.md` (look at the fenced YAML bullets) |
| Mermaid diagrams (flowchart, sequence) | `products/payments/concepts/payment-lifecycle.md`, `developers/v2/identity-api/README.md` |
| Layout `width: wide` + cover image | `home/README.md`, `developers/v2/README.md` |
| Card-tables for navigation | `home/README.md`, `partners/README.md` |
| Conditional content via `{% if visitor.claims... %}` | `products/payments/accept-payments/take-a-payment.md` |
| Tabs and steppers used together | `developers/v2/getting-started/quickstart.md`, `developers/v2/getting-started/authentication.md` |
| Webhook docs with code samples | `developers/v2/webhooks/verifying-signatures.md` |
| Reusable content includes | `home/.gitbook/includes/persona-switcher.md` |
| `.gitbook/vars.yaml` variables | Any space's `.gitbook/vars.yaml` |
| Grouped SUMMARY.md (sections via `## Heading`) | Any of the per-space `SUMMARY.md` files |
When you need a pattern not represented in the bundled snapshot (e.g. legacy/beta versioned API spaces side-by-side, the full external-content article catalogue), `structure.json` is the authoritative source for shape, and PRUNE-NOTES.md describes the patterns those omissions represented.
After scaffolding:
```bash
cd my-docs
git init
git add .
git commit -m "Initial scaffold"
```
If the user wants a remote and `gh`/`glab` is available:
```bash
# GitHub
gh repo create <name> --private --source=. --push
# GitLab
glab repo create <name> --private && git push -u origin main
```
If neither tool is available, **say so explicitly** before scaffolding finishes. Two valid paths:
- **Local-only repo + manual remote step in handoff.** Commit locally, leave the user a "Step 0" in their handoff that says: *"On your machine, create a private GitHub or GitLab repo named `<name>`, then `git remote add origin <url> && git push -u origin main` from this directory."* Put this above the GitBook UI steps — they need the repo pushed before Git Sync can connect to it.
- **Ask the user to install `gh` or `glab`.** If they're going to be doing more sites, the tool is worth having.
Don't quietly default to local-only — a repo with no remote and no instructions about how to add one is a footgun the user will discover when they try to wire up Git Sync.
## Migration and content quality
Most real builds aren't greenfield — they're migrations from another docs platform (Mintlify, Docusaurus, ReadTheDocs, an older GitBook), or restructurings of existing markdown. These have their own discipline that's distinct from "make a new site." Get this wrong and you produce something that *looks* like a docs site but reads like a machine output.
The full workflow is in `references/migration-from-other-platforms.md`. The headlines:
**Mirror the source before reimagining it.** When the user has an existing live docs site, fetch the rendered landing pages and look at them before generating any homepage content. The current IA is the spec; the user's reasons for it usually aren't visible from a folder of markdown alone. Inventing card grids, hero blocks, and "what's new" sections from scratch when the source already had a working answer is the most common content-quality failure mode.
**A bulk export is rarely a complete content source.** Mintlify's `llms-full.txt`, ReadTheDocs HTML scrapes, and similar AI-friendly exports often strip visible content (custom components rendered to raw markup, AI-prompt blocks unrolled inline, parameter names dropped from API tables). After bulk conversion, sample-check the rendered pages against the original site and flag where content is missing — don't pretend the export is the whole story.
**Anchor pages, not all pages.** Migrating 280 pages doesn't mean hand-crafting 280 pages with GitBook idioms. The right move is to bulk-convert the long tail, then deliberately rebuild 4–6 anchor pages — homepage, top-level landing per space, the marquee how-to — using the full block ecosystem. The rest can be brought up to standard iteratively.
**Format-pass is a documented step.** Naive markdown conversion leaves artifacts (foreign component tags, malformed code fences, broken internal links). After bulk conversion, run a clean-up pass before the first commit — remove unsupported components, normalize fences, rewrite `/docs/...` paths to GitBook URLs or relative paths. Skipping this produces a repo that *almost* renders.
**Don't auto-generate frontmatter you don't have.** When the source had no icons, don't auto-pick icons from URL slugs — you'll produce a sea of mismatched cog icons. When the source had no description, leave the field blank; don't stub it with `Source: <url>` (that text leaks into sidebar previews and search).
**OpenAPI-first for API references.** If the migrated site has API reference content and you can get an OpenAPI spec (or generate one from their codebase), route the entire reference space through `builtin:openapi`. A 70-page hand-converted reference is almost always worse than a 3-file auto-generated one.
**Internal-link conversion is a sweep, not a per-page concern.** After the structure is known, walk every markdown file and rewrite `/docs/...` links to either relative `.md` paths (within a space) or `https://app.gitbook.com/s/<spaceId>/<path>` URLs (across spaces). Doing this per-page-as-you-go produces inconsistent links; doing it as one sweep with a slug-to-path manifest is much more reliable.
**Be careful with helper scripts that regenerate content.** If you're using a converter or SUMMARY-generator, make it idempotent by default. Skip files that already exist. A second run that clobbers hand-tuned homepages is a footgun. Never run `rm -rf <space>/` on a directory that might contain hand-edited content; if you must regenerate, write to `<space>/_generated/` and merge or diff.
## Driving GitBook to build the site
The steps below are described as outcomes, not endpoint calls — use whichever transport you settled on in "How you can talk to GitBook" above. On the REST API path, the exact endpoints, request bodies, and expected responses for every step are in `references/api-cheatsheet.md`; read it before making any calls — the schemas are nuanced (especially customization). On the MCP path, the equivalent tools cover the same steps — read their own schemas rather than looking up REST paths.
### The standard sequence for a new site
1. **Verify access and find the org**: confirm the authenticated user, then list the orgs.
2. **Create the site** with `{title, type, visibility, spaces?}`. **Default to Ultimate** (`type: "site"`; the plan tier is set on the site after creation or via the org's billing). Use `type: "basic"` (free) only when the user explicitly opts in. Don't include `spaces` if no spaces exist yet — you can add them later.
3. **Decide how spaces will come into being.** Two paths:
- **Site-wide Git Sync (recommended, default)**: tell the user to open **Git Sync** from the site sidebar once, connect the repo/branch, and map each space to its directory under **Content mapping**. This single UI pass creates/links every space to the site and wires up sync for all of them at once. The skill's job is to give exact, copyable instructions for that one pass. See `references/git-sync-handoff.md`.
- **Programmatic-first**: create empty spaces directly, add them to the site as site-spaces, and use content import or template application to load content. The user will still need to wire Git Sync in the UI later if they want bidirectional sync — and when they do, site-wide is still the default to point them at, not one space at a time.
4. **Add sections** (multi-space sites with grouped navigation): a section is created by associating a space with a title and optional icon.
5. **Resolve cross-space link sentinels**: if the scaffolded markdown contains `XSPACE_<KEY>` placeholders (which it should, for any link that crosses a space boundary), now is when you substitute them for the real space IDs returned by step 3 or 4. See `references/cross-space-links.md` for the substitution script. Commit and push the changes — the next Git Sync run picks them up.
6. **Apply customization** (branding) — the full schema is broad: theme preset, colors (each as a `{light, dark}` themed pair), favicon, header (logo, primaryLink, links), footer (groups of links, copyright), themes (default light/dark, toggleable), AI mode, PDF export, and more. Recipes for common branding scenarios are in `references/customization-recipes.md`. Only change the fields you mean to — fetch the current settings first, modify in memory, and write the full result back rather than guessing at a partial payload.
7. **Verify**: fetch the site's structure to confirm the final tree, and its customization to confirm settings.
### Multi-language sites and auto-translated spaces
GitBook supports **auto-translated site-spaces**: a single English space (synced from Git) can be paired with computed translations in other languages. The translations are not separate spaces in the Git repo — they live entirely in GitBook and are configured through the UI under each section's settings. They show up as additional `site-space` objects under the same section, each with a different `language` and no `gitSync` field.
What this means in practice:
- **Don't scaffold per-language directories in the repo.** The Git repo has one space per topic, in English. The skill writes one set of markdown files per content area, full stop.
- **Each section can hold many site-spaces.** A "Payments" section might contain `Payments` (en, git-synced), `Payments (FR)` (fr, computed), `Payments (DE)` (de, computed), etc. The structure response will list all of them; only the English ones need a Git Sync handoff.
- **`localizedTitle` shows up everywhere.** Sections, section-groups, header links, footer links, and the site title itself all carry a `localizedTitle: {de: "...", fr: "...", ...}` map. When reading the customization, expect to see translations even for fields the user only set in English. Don't strip these out unless asked.
- Auto-translation is a UI-only feature today. If the user wants it enabled on a section, surface that as part of the site-wide Git Sync handoff: "After Git Sync is configured, go to **Site → Sections → Payments → Translations** and enable the languages you want."
When the user asks for "a docs site in five languages", the answer is one English content tree in Git plus auto-translation enabled per section in the UI — not five copies of the markdown.
### Section groups
A site's structure has three possible levels of nesting in the navigation:
1. **Site-spaces** at the root (a flat site, no sections)
2. **Sections** containing site-spaces (the typical multi-space site)
3. **Section groups** containing sections containing site-spaces (used to bucket related sections, e.g. "Products" group containing Payments / Identity / Connect sections)
The site's structure response is recursive — a section-group's `sections` array can hold both sections and other section-groups. When designing the structure, use section-groups only when there are 3+ closely related sections that benefit from being visually grouped in the top nav. For a 2-section site, sections at the root level are clearer.
### Updating an existing site
When asked to modify a site that already exists, *always* fetch the current state first:
- Site metadata
- Structure (sections + spaces)
- Customization (site-level or per-site-space), for branding
Then make targeted changes rather than wholesale replacements. Don't replace whole customization payloads if you only mean to change one field — fetch the current settings, modify in memory, and write the full result back.
### When to use content import vs. Git Sync
- **Content import** is for ingesting external content (a website URL, a set of files) into a space. It's good for one-shot migrations from another doc tool.
- **Git Sync** is for ongoing two-way sync between a Git repo and a site (or, as a fallback, an individual space). This is what we're optimizing for in the standard flow.
- If the user has already-good content sitting outside both Git and GitBook (e.g. a Notion export), import it, then optionally turn on Git Sync afterward.
## Branding and customization
The `SiteCustomizationSettings` schema is large. The bundled `references/example-site/customization.json` is a real export from a production-style demo and is the most useful reference — read it before composing any customization payload. It shows how all the nested fields fit together, how `localizedTitle` maps work, and how conditional header links are structured.
The full field listing, schema quirks (`styling.background` required-but-vestigial, `header.links[]` requiring `links: []`), `ContentRef` formats for header/footer links, and conditional link patterns are all in `references/customization-recipes.md` — see "Field cheatsheet" and Scenarios 4–5. Premium and Ultimate features (custom logos, custom fonts, semantic colors, footer logo, advanced customization) will be rejected on free sites — handle gracefully; on the REST path see `references/api-cheatsheet.md` for the exact error responses.
`references/customization-recipes.md` has worked examples for: minimal brand pass (just colors + favicon), full brand with logos and fonts, dark-mode-only with toggle, and AI-assistant enabled with suggested prompts.
For a complete real-world payload to learn from, `references/example-site/customization.json` is the customization export of a production site bundled with this skill. Reading it is the fastest way to see how all the fields fit together in practice — much more useful than the abstract schema. Don't paste it wholesale into a new site; use it as a model for shape and field selection.
For a real example of the structure response (sections, section-groups, multi-language site-spaces), see `references/example-site/structure.json` alongside it.
## The Git Sync handoff
This is the part that has to feel polished. Once the repo is pushed and the site exists, generate one clear handoff for the whole site — not one block per space. The user needs:
1. The repo URL and branch name (usually `main`)
2. The site's **Project directory** — where `gitbook-docs.yaml` lives (blank/root unless this is a larger monorepo)
3. The initial sync direction — almost always **GitHub → GitBook** (or GitLab → GitBook), since the repo is the source of truth at this point
4. The **content mapping** — each space's title paired with its directory (e.g. `Guides` → `./guides`, `API Reference` → `./api-reference`)
`references/git-sync-handoff.md` has a template you can fill in and present to the user: connect once, map every space in the same pass. Render it as a single numbered list, not a wall of prose, and not repeated per space. Only add a second handoff block if a specific space needs to be pulled out into its own independent repo/branch — see "When a space needs its own repo or branch" in that file. After the user finishes, ask them to confirm — at that point you can check each space's sync state programmatically to verify (there's no site-level status endpoint yet, so this is still a per-space check under the hood).
## Common mistakes to avoid
- **Don't put the PAT in any file Claude writes.** Always read it from the environment.
- **Don't silently swap the content source.** If the repo or folder the user named can't be read (remember: private repos return 404, same as nonexistent ones), stop and ask — never proceed with a lookalike public repo. See "Verify the content source before building."
- **Don't try to set up Git Sync programmatically.** It's UI-only regardless of transport — always route through the UI handoff. (`installGitSyncProviderOnTarget` exists in the API but doesn't remove the OAuth step and isn't yet exposed via MCP — don't route around the handoff because it looks tempting.)
- **Don't hand off Git Sync one space at a time.** Site-wide Git Sync is the default — one connection, one content-mapping pass for every space. Fall back to per-space Git Sync only when a specific space needs an independent repo or branch.
- **Don't paste an entire customization payload from memory.** Fetch the current state, modify it, then write the full result back. Schemas evolve and you'll write fewer bugs this way.
- **Don't create a space for every section of content.** A space is a heavy unit (it has its own URL slug, sync, settings). Pages and folders within a space are the right tool for sub-grouping.
- **Don't skip the structure-plan-and-confirm step**, even when the user is in a hurry. Restructuring a published site is painful.
- **Don't over-format the SUMMARY.md.** GitBook's parser is strict about it. Defer to the rules in `write-docs`.
- **Don't finish a change-request edit without both links.** See "After a change-request push: two links are mandatory" — the CR diff link alone is an incomplete answer.
## Reference files
- `references/api-cheatsheet.md` — the complete set of API calls used by this skill, with curl-style request bodies and expected responses
- `references/site-structure-design.md` — heuristics and worked examples for going from raw inputs to a space/section/page plan
- `references/migration-from-other-platforms.md` — pre-flight, source-platform mappings (Mintlify, Docusaurus, GitBook v1, RTD), anchor-pages strategy, format-pass, internal-link sweep. **Read this before any migration build, not after.**
- `references/block-ecosystem.md` — which GitBook block to reach for in which content situation, with a decision table and worked examples (Updates, Mermaid, OpenAPI auto-gen, layout flags, card-tables, conditional content, includes, vars). **Read this before generating any non-trivial page.**
- `references/cross-space-links.md` — the sentinel-and-resolve workflow for cross-space links in markdown, with a working substitution script
- `references/git-sync-handoff.md` — the template for the user-facing Git Sync setup instructions, site-wide first with per-space as the documented fallback
- `references/customization-recipes.md` — worked branding payloads for common scenarios
- `references/example-site/` — a pruned snapshot (~150 files) of a real production-style GitBook site repo (markdown, `SUMMARY.md`s, `.gitbook/` configs). Read `PRUNE-NOTES.md` inside it first — it explains what's kept, what's dropped, and lists high-signal files for specific patterns.
- `references/example-site/customization.json` — the customization export from that site, illustrating a complete real-world branding payload
- `references/example-site/structure.json` — the structure export, showing sections, section-groups, and multi-language site-spaces (English git-synced + auto-translated)