Bring an existing business
Open in
An import moves an API you already sell off another billing system: its OpenAPI file becomes your file, and its Customers arrive with their Keys.
Five steps to move a service
- Turn the old service's OpenAPI file into your file with
npx apis.dev import openapi, then set the prices it charged. - Plan the Stripe import with
--plan. It counts each kind, names every conflict, and changes nothing. - Settle each conflict with
Import.resolve: keep one row, merge the two, or skip it. - Run it with
Import.run. Nothing a Customer sees changes until every row is in. - Point the old hostname at the product as one of your Domains, so old callers change no line.
$ npx apis.dev import openapi ./openapi.yaml
✓ 2 Nouns from 2 schemas: Company, Deal
✓ 7 Verbs from 9 operations; 2 listed in import-report.md
✓ wrote definition.ts
$ npx apis.dev import stripe --plan
✓ 312 customers · 287 subscriptions · 1 conflictThe plan names its one conflict and the fixes Import.resolve takes.
HTTP/1.1 200 OK
Content-Type: application/ json
X-Cost: price= 0, currency= "USD"
X-Request-Id: req_6d2f19c0b8a4e37d5f10
{
"id": "imp_3",
"kind": "stripe",
"state": "uploaded",
"rows": { "customers": 312, "subscriptions": 287, "members": 0, "keys": 0 },
"plans": { "Team": 241, "Free": 39, "kept": 7 },
"conflicts": [
{ "row": "sub_1Q4b", "says": "A price of 39 a month matches no plan",
"fixes": ["keep", "skip"] }
]
}That is GET https:. keep holds the price by grandfathering.
The file the first step writes is whole, and check passes it once the prices are set.
import { define } from 'apis.dev'
/** Deals for each Customer, imported from the OpenAPI file of the service it replaces. */
export default define({
name: 'pipeline', domain: 'pipeline.apis.dev',
Company: { name: 'string', domain: 'url!#', deals: '<-Deal.company' },
Deal: {
title: 'string', amount: 'decimal(12, 2)', company: '->Company.deals',
stage: { open: [], qualify: 'opened', win: 'qualified', lose: ['opened', 'qualified'] },
},
offer: {
Free: { month: 0, seats: 1, includes: { 'Deal.open': 25 } },
Team: { month: 49, seats: 5, includes: { 'Deal.open': 1000 } },
'Deal.open': 0.001,
},
})What each kind becomes
| You bring | It becomes | What a Customer notices |
|---|---|---|
| An OpenAPI 3 file | A definition.ts: a Noun per schema, a Verb per operation, and a report of what did not map | Nothing |
| Stripe customers | Customers, each keeping its Stripe customer id; cards stay in Stripe and are never copied | Nothing; the next receipt carries your brand |
| Stripe subscriptions | Each on the plan whose price matches, or on its own price kept by grandfathering | The same price on the same day |
| Members | Members of each Customer's org, with password hashes kept or a sign-in link sent | The same sign-in |
| Keys | Each Key's SHA-256 digest and prefix, tied to its Customer | The same Key keeps working |
An operation that does not map, such as a callback or an untyped body, is listed with its path. Write it by hand, or front it from the API that serves it now.
When an import stops
A run is idempotent, so one that stops resumes at the row it stopped on. Each stop is a Problem with its cure.
| What happens | The answer |
|---|---|
| The file is not OpenAPI 3.0 or 3.1, or is past the limits | 422 openapi_invalid, with the line it stopped at |
Import.run while the plan still names conflicts | 409 import_conflicts, naming each |
| A second import of the same kind for one product | 409 import_running, naming the one running |
| A row fails, such as a member whose email another org holds | The import turns failed, naming the row and its Problem; fix it and run again |
| A password hash in a scheme the platform does not keep | Not a failure: that member gets a sign-in link instead |
| Stripe access is withdrawn during a run | failed at the next row; nothing already imported is touched |
Import.undo after 7 days | 409 undo_closed; export instead |
An undo removes what the import created and releases every Stripe match. It never refunds money already charged; the refund rule does that.
Sizes and speed
| What | How much |
|---|---|
| An OpenAPI file | 10 MB and 2,000 operations |
| Rows in one Stripe import | 1,000,000 customers, with their subscriptions |
| Speed | About 1,000 rows a second, with progress on Import.get |
| Password hashes kept | bcrypt, scrypt and argon2; any other sends a sign-in link |
| Undo | Within 7 days of done |
Every import has its export
What arrives by import leaves by export, in the same form, so a product is never locked in.
| Brought in as | Taken out by Product.export as |
|---|---|
| An OpenAPI file | definition.ts, each Version, and its OpenAPI document |
| Stripe customers and subscriptions | The same Stripe account, which stays yours throughout |
| Members | One export per Namespace, with their sign-in methods |
| Keys | Their digests, so the next server checks every Key without holding one |
Leaving APIs.Dev lists the rest, and what Customers keep holds for them either way.
A product's life
A product from before its first Function to after its last call: pre-orders and the interest list, what it tells you, growth, sunset, transfer and export.
What Customers keep
What Customers keep when a seller stops, leaves or cannot act: notice, service until the close, their records, held funds returned and receipts that verify.