Faivelo
Dashboard
AI & Developers

Partner API

Resell Faivelo mailboxes under your own brand. Customers, domains, mailboxes and health, from one API.

Faivelo partners resell mailboxes to their own clients: agencies putting their clients on email, hosting companies adding a modern mailbox, and platforms that want an inbox inside their own product. One API creates a customer, their domain and their mailboxes. Your clients see your name, and you set your price.

Two ways to run it

It is up to you how much of Faivelo your clients see.

  • Use our white-label apps. The partner console at partner.faivelo.com manages customers, domains and mailboxes by hand, and your clients get a webmail, calendar and Drive at your logo and colours, with every email from us in your name and their language. No code required.
  • Build your own on the API. Keep clients inside your own product: create customers, domains and mailboxes from your code, read DNS status and health, and pull the usage report into your billing. Your clients use any mail app, or a front end you build.

Both are included in the same per-customer price, and you can mix them. Everything the console does is an API call, so starting by hand and automating later loses nothing.

This page covers the API. The console's Guides section explains the rules of being a partner in plain language.

Partner keys

Partner accounts get a third kind of API key, partner, issued from Developers in the partner console once your account is approved. A partner key:

  • acts on your end customers, never on your own account;
  • holds only the scopes partner:read and partner:write;
  • works under /api/partner, and is refused by /api/v1 and the MCP server.

Send it as a Bearer token, like any other key:

Authorization: Bearer fvl_live_xxxxxxxxxxxxxxxxxxxx

A customer that is not yours answers 404, never 403, so a key learns nothing about other accounts.

Two keys: one manages customers, one uses the mailboxes

A partner integration holds two kinds of key, and it helps to keep them apart:

Partner keyCustomer key
MintedPartner console, DevelopersPOST /customers/{id}/api-keys (faivelo.partner.apiKeys.create)
Acts onYour end customers: create them, add domains, create mailboxes, reset passwords, read health and reportsOne customer's mailboxes: read, search and send mail, folders, contacts, signature, profile picture, auto-reply
Scopespartner:read, partner:writemailboxes:read, mail:read, mail:send, mail:write, or only the mail:* ones when bound to a single mailbox
Base path/api/partner/api/v1

The partner key never reads mail and is refused by /api/v1; a customer key never provisions anything. To render an inbox or let a client edit their signature inside your portal, mint a customer key for that customer (optionally bound to one mailbox), keep it server-side, and call the /api/v1/mailboxes/{address}/... endpoints with it. Every endpoint listed under Mailbox settings from your product works with a customer key: reading takes mail:read, changing takes mail:write. It stops working while the customer is paused or suspended and when you revoke it. Sandbox customers have no mail store, so customer keys need a live customer.

Sandbox mode

Mint a partner key with Sandbox ticked and it starts with fvl_test_ instead of fvl_live_. Same endpoints, same responses, but every customer it creates is a sandbox: domains come back verified the moment you add them, mailboxes exist only as rows with credentials, nothing touches the mail server, no seat lands on your invoice, no email goes to anyone, and nothing counts in the reputation ladder, reports or outbox. Live and sandbox customers never see each other.

One call exists for sandbox only: DELETE /customers/{id} erases a sandbox customer. Anything you leave behind is erased after 30 days.

The console has the same switch under your organisation name: in Sandbox mode it shows sandbox customers only, and a New customer there creates a sandbox.

The onboarding loop

Every partner integration is the same four calls. In the SDK:

import { Faivelo } from 'faivelo'
const faivelo = new Faivelo(process.env.FAIVELO_PARTNER_KEY!)

// 1. Create the customer. You attest, per customer, that their lists are
//    first-party and that you accepted the end-customer terms for them.
const customer = await faivelo.partner.customers.create({
  email: 'owner@smiledental.example',
  companyName: 'Smile Dental',
  attestation: { firstPartyListsOnly: true, termsAcceptedOnBehalf: true }
})

// 2. Add their domain on a plan (billed per domain; mailboxes are
//    unlimited). The response lists the DNS records to show them.
const domain = await faivelo.partner.domains.add(customer.id, {
  domain: 'smiledental.example',
  brandName: 'Smile Dental',
  plan: 'GROWTH'
})
// domain.dnsRecords -> [{ type: 'MX', name: ..., value: ..., verified: false }, ...]

// 3. Poll until the records are live.
const status = await faivelo.partner.domains.get(customer.id, 'smiledental.example', { verify: true })

// 4. Create mailboxes. Password and IMAP/SMTP settings come back once.
if (status.verified) {
  const inbox = await faivelo.partner.mailboxes.create(customer.id, 'smiledental.example', {
    localPart: 'hello',
    displayName: 'Smile Dental'
  })
  // inbox.credentials.password, inbox.credentials.imapServer, ...
}

The same loop with curl is in the API reference.

Plans and pricing

You pay per live domain, on the plan you choose for that domain, once its DNS has verified. A customer is only a folder for your client: it costs nothing by itself. Your own domains are billed the same way, on the 10 GB plan unless you pick another. Plans are named by their storage; the API value is in brackets.

Plan (API value)Per domain, per monthStorage shared across the domain
10 GB (STARTER)$310 GB
50 GB (GROWTH)$650 GB
200 GB (PRO)$12200 GB
1 TB (BUSINESS)$241 TB

Mailboxes are unlimited on every plan; the domain's mailboxes, contacts and Drive share its storage. Pass plan when you add a domain (default: the customer's current plan, which is the plan you gave when creating the customer, else your partner account's default). Change one domain with POST /customers/{id}/domains/{domain}/plan (faivelo.partner.domains.setPlan); a downgrade is refused with 409 PLAN_DOES_NOT_FIT while the domain stores more than the new plan holds. POST /customers/{id}/plan (faivelo.partner.customers.setPlan) moves every domain of a customer at once. On a plan agreed with Faivelo outside the published prices, plan changes go through support: both calls answer 403 PARTNER_CUSTOM_TERMS (re-sending a domain's current plan still succeeds, and sandbox customers are unaffected).

Every domain past your first 20 is 20% off, automatically, most expensive plans first. 1 TB (BUSINESS) domains count toward the 20 but are never discounted. A custom mail server name your clients see in their mail apps is an add-on at $19 a month per name, billed once it is live. Domains are counted each night and on every change; a paused customer's domains are not counted. Prices are introductory and change only with 30 days notice. Partners approved before November 2026 keep their earlier terms until notified; the Billing page in the console shows the terms that apply to you.

Choosing the password

Both mailbox calls take an optional password (8 to 128 characters; the mail server also refuses weak ones):

  • POST /customers/{id}/domains/{domain}/mailboxes with { "localPart": "hello", "password": "..." } creates the mailbox with that password.
  • POST /customers/{id}/domains/{domain}/mailboxes/{address}/reset-password with { "password": "..." } sets it; with no body a random one is generated.

When you choose the password the response still carries the IMAP/SMTP settings, but credentials.password is null: you already hold it, and we do not echo secrets we did not generate. When we generate it, it is returned exactly once.

The customer has no password and no Faivelo dashboard by design. They use the webmail, which wears your brand, and you operate the account through the API or the console.

What the customer sees

Each customer domain gets a webmail at its own subdomain. The look is resolved per domain, in this order:

  1. The domain's own name and logo, set with PUT /customers/{id}/domains/{domain}/branding.
  2. Your partner brand, set once in the console: logo, sign-in photo, theme, favicon.
  3. Faivelo's stock look.

The same branding call takes a defaultAvatar: the profile picture every mailbox on the domain shows until the person sets their own. In the console, it is under the domain's Default profile picture.

The sign-in page and the inbox carry a small "Powered by Faivelo" line. Everything else is yours.

Your own mail server names

By default your customers' mail apps connect to mx.faivelo.com, and that name appears in their IMAP/SMTP settings, setup emails, Apple profiles and autodiscover records. You can replace it with names under your own domains, $19 a month per name, billed only while the name is live:

  1. POST /mail-hosts with { "host": "mail.yourcompany.com" } (faivelo.partner.mailHosts.add), or add it in the console under Settings. The response's status.setup object is the one record to publish.
  2. Publish it: an A record from mail.yourcompany.com to the IP in setup.value (Faivelo's mail proxy). No other A or AAAA record for the name.
  3. The proxy issues a Let's Encrypt certificate for the name within a few minutes of the record resolving, and renews it on its own. GET /mail-hosts (faivelo.partner.mailHosts.list) reports each name's dns.verified, certificate.valid and live; POST /mail-hosts/{host}/check (mailHosts.check) re-runs both checks now.

The first name you add is your default: every customer sees it unless you point them at another one with PUT /customers/{id}/mail-host (mailHosts.setForCustomer), handy when a client wants mail.theirbrand.com. POST /mail-hosts/{host}/default makes another name the default, DELETE /mail-hosts/{host} removes one (its customers go back to the default).

Customers switch to a name only when both checks pass, and fall back on their own if either stops passing (to your default, then mx.faivelo.com), so a half-finished setup never puts a name in a customer's mail app that their phone cannot reach. Customer domains keep their MX at mx.faivelo.com; only the client-facing names change. The older single-name calls (GET/PUT/DELETE /mail-host, faivelo.partner.mailHost) still work and act on your default.

Ports differ from mx.faivelo.com: your names serve IMAP 993, SMTP 465 and POP3 995 over SSL/TLS (plus CalDAV/CardDAV on 443). There is no STARTTLS on 587 or 143 behind them. Every credential the API returns, every setup email and Apple profile already carries the right ports (ports on each name shows them), so there is nothing to translate on your side. Customer domains created while a name is live get _imaps and _submissions SRV records pointing at it; earlier domains keep theirs.

What each name's fields mean:

FieldMeaning
dns.verifiedthe name resolves to the proxy and nowhere else
certificate.validthe proxy presents a valid certificate for the name on port 993; certificate.expiresAt / daysLeft come from that certificate
liveboth of the above, so activeHost is the name; only live names are billed
activeHost, portswhat customers are shown right now
errorwhy the last check failed, in plain words
setupthe A record to publish (type, name, value)
isDefaultwhat customers without their own pick see

Storage

Storage is pooled per domain by the domain's plan. GET /customers/{id} reports the customer's total as storage (bytes used across every domain, the sum of the domains' pools as limitBytes, and what is left), and each domain reports its own. Inside a domain's pool you can cap the domain lower and quota a mailbox:

LevelWhere you read itHow you set it
Customerstorage on GET /customers/{id}The sum of its domains' plans
Domain planplan and storage on the domain endpointsPOST /customers/{id}/domains/{domain}/plan (faivelo.partner.domains.setPlan)
Domain capstorage and storageLimitBytes on the domain endpoints and in domainListPUT /customers/{id}/domains/{domain}/storage with { "limitBytes": 53687091200 } (faivelo.partner.domains.setStorage); null removes the cap
Mailboxstorage and storageLimitBytes on the mailbox list and GET .../mailboxes/{address} (faivelo.partner.mailboxes.get)PUT .../mailboxes/{address}/storage with { "limitBytes": 5368709120 } (faivelo.partner.mailboxes.setStorage); null restores the plan default

Every figure is in bytes. usedBytes comes from the mail server (one batched read per domain, cached for a minute), and the mailbox limit is the quota the server enforces, so a mailbox that fills up stops receiving until it is cleaned out or given more room.

The rules, all answered with 422 when broken: a domain cap fits within the domain's plan storage and covers the explicit quotas already set on its mailboxes; a mailbox quota fits within its domain's cap (else the plan storage) together with the explicit quotas of the domain's other mailboxes. Mailboxes you never quota explicitly get the plan's default per-mailbox ceiling (the whole pool below 1 TB, a quarter of it on Business), clipped to the domain cap; quotas are limits, not reservations, so the defaults are not summed against the pool.

A customer key reads the same numbers for one mailbox on GET /api/v1/mailboxes/{address} (storage), but cannot change them.

Signing a client into webmail

When your portal has already authenticated the client, send them straight into their mailbox: POST /customers/{id}/domains/{domain}/mailboxes/{address}/webmail-session (faivelo.partner.mailboxes.createWebmailSession) returns

{ "url": "https://smiledental.faivelo.com/api/webmail/auth/sso?token=...", "expiresAt": "2026-10-17T10:02:00.000Z" }

Redirect the browser to url right away. The link lives two minutes and works once; a second visit, or a visit after it expires, lands on the normal login page, so a link that leaks from a log is worth nothing. It opens on the domain's own webmail host when it has one, so the client sees your brand, and every link you mint is on the customer's audit trail. Sandbox customers answer 400 (no webmail) and an inactive mailbox 409.

Mint the link server-side at the moment of the click and never store it. There is deliberately no call that checks whether a password is right: a password oracle invites guessing, and the sign-in link covers the case.

Sending health

Every customer is an isolated sending tenant with its own reputation. GET /customers/{id} reports:

FieldMeaning
statusactive, paused or suspended
lockedByWho set the current sending lock: partner (you), faivelo (an abuse control), or null
ratesThe customer's own sends, bounces and complaints over the last 30 days, and whether it is over the pause line on its own numbers
reputationThe tenant's sending state, ours and the mail provider's
relayCeilingtrial until the customer earns the standard ceiling on clean history
domainListPer-domain verification, enforcement tier and rates

Use POST /customers/{id}/pause to hold a customer's sending yourself and resume to lift it. A lock set by a Faivelo abuse control is not lifted by resume; the console shows what it needs.

Bounces and complaints count against the whole partner account as well as the customer. A customer that crosses the line is paused on its own; a pattern across customers pauses provisioning for the partner. The thresholds are published in the console's Guides and never change without notice.

The outbox

GET /outbox (faivelo.partner.outbox.list) lists every system email Faivelo sent about your customers: set-up mail, credentials, recovery, notices. Filter by customer, category, status and date. GET /outbox/{id} (faivelo.partner.outbox.get) returns the body, except for credential and sign-in link mail, which never stores one.

Webhooks

Rather than polling, add an endpoint in the partner console under Developers. One endpoint hears every customer, each payload carries a customer_id, and you get customer events (customer.created, customer.plan_changed, customer.paused, customer.resumed) next to the account, domain and delivery events. If you are building an inbox into your own product, message.received tells you the moment mail lands. Signatures, retries and the payloads are in the Webhooks guide.

Billing your clients

You bill your clients; Faivelo bills you per live domain, once its DNS has verified. GET /reports/usage?month=2026-09 (faivelo.partner.reports.usage) returns one line per customer for the month: days live, each domain with its plan and list price (domainPlans, monthlyCents), mailboxes, storage, sends, bounces and complaints. Add format=csv (faivelo.partner.reports.usageCsv) to download a file. The console's Reports page shows the same table.

Mailbox settings from your product

With a customer key (see Two keys), your portal can manage what a client would otherwise set in webmail. All paths are under https://faivelo.com/api/v1, and the request and response shapes are in the API reference.

MethodPathScopeWhat it does
GET/mailboxes/{address}/contactsmail:readThe people the mailbox corresponds with, most frequent first
GET/mailboxes/{address}/foldersmail:readFolders with message counts
POST/mailboxes/{address}/foldersmail:writeCreate a folder (name, optional parent path)
PUT/mailboxes/{address}/folders/{path}mail:writeRename a folder (name); nested paths keep their slashes
DELETE/mailboxes/{address}/folders/{path}mail:writeDelete a folder and its mail
GET/mailboxes/{address}/signaturemail:readThe HTML signature, and whether the domain enforces a template
PUT/mailboxes/{address}/signaturemail:writeSet the signature (signatureHtml; scripts and javascript: links are stripped)
GET/mailboxes/{address}/avatarmail:readThe profile picture as base64
PUT/mailboxes/{address}/avatarmail:writeUpload a PNG, JPEG or WebP up to 500 KB (base64, mimeType)
DELETE/mailboxes/{address}/avatarmail:writeRemove the picture
GET/mailboxes/{address}/auto-replymail:readThe out-of-office reply
PUT/mailboxes/{address}/auto-replymail:writeSet it: enabled, subject, textBody or htmlBody, optional fromDate and toDate

Inbox, Sent, Drafts, Trash, Junk and Archive cannot be renamed or deleted (403). The auto-reply is kept by the mail server, which answers each sender once while it is on and never replies to mailing-list or automated mail; send the whole state on every PUT ({ "enabled": false } switches it off).

A customer key does not reset passwords: that stays with the partner key, so a leaked mailbox key cannot lock a client out.

Migrating mail from your server

Moving a customer domain off your own IMAP server (Dovecot, cPanel, a previous host)? You hold every source password, so the people on the domain never touch anything. One domain at a time:

  1. Test — POST /customers/{id}/domains/{domain}/migrations/test (faivelo.partner.migrations.test) with the source server and the mailboxes as CSV text (csv: email,password[,newPassword], one per line, an optional header) or JSON rows. Faivelo logs into every source mailbox (five at a time, at most 100 rows per call) and answers per row: whether the login worked, how many messages and bytes are there, whether the Faivelo mailbox already exists, plus storage (bytes needed against what the customer has left, and any mailbox larger than the quota a new one gets). Nothing is created.
  2. Create — POST /customers/{id}/domains/{domain}/migrations (faivelo.partner.migrations.create) with the same body (at most 500 rows; pass the test's byte counts as expectedBytes to be refused up front when the mail does not fit). Missing mailboxes are created with the password from the file, so people keep signing in as before; newPassword sets a different one. Existing mailboxes are reused and keep their password. Mailboxes created this way are bounded by the request's 500 rows rather than the usual 50-per-day figure for POST .../mailboxes, still within the customer's plan and storage, and they do not count toward that figure, so you can add mailboxes through the API the same day. The source passwords are stored encrypted and held, the copy starts, and the migration id comes back. Sending the same rows again changes nothing but the stored source passwords and queues another sync.
  3. Watch — GET /customers/{id}/domains/{domain}/migrations/{migrationId} (faivelo.partner.migrations.get; migrations.list for the domain) for per-mailbox progress (status, copiedMessages of totalMessages, bytesCopied, lastSyncedAt, syncPending, error) and totals (syncPending, syncedUpTo). phase says where the domain stands: copying (a pass is queued or running), idle (waiting for you), finishing (the last pass is running), finished (credentials deleted). copiedMessages accumulates across passes, so it can exceed totalMessages. The list endpoint returns the domain's migrations the same way.
  4. Switch MX once the first copy is done, then POST .../migrations/{migrationId}/sync (faivelo.partner.migrations.sync) to copy what arrived on the old server meanwhile. Sync as often as you like while the credentials are held.
  5. Finish — POST .../migrations/{migrationId}/finish (faivelo.partner.migrations.finish) queues one last sync; when it completes, every source password is deleted from Faivelo (finishedAt). After that nothing more can be copied; a new migration would need the passwords again. A sync or finish that the engine refuses answers 409 with a code (FINISHING, FINISHED, NOT_HELD), never a 500; a create on a finishing or finished domain starts a new migration.

Starting a migration (test and create) is switched on per platform by Faivelo; until then both answer 403 with code: MIGRATION_NOT_AVAILABLE. Reading, syncing and finishing an existing migration always work.

The source passwords never appear in a response, a webhook or a log. The test endpoint is limited to 10 calls per 10 minutes per partner, since each call is a round of logins against a server that is not ours. A sandbox customer cannot be migrated into (there is no mail store). The same flow is in the console, on the customer's domain, as Migrate mail.

Bringing the address book along

The mail comes through the migration; the person's contacts come as the vCard file their old webmail exports (Roundcube: Contacts → Export; cPanel and most hosts bundle Roundcube). Per mailbox:

POST /customers/{id}/domains/{domain}/mailboxes/{address}/contacts/import with the file as the body (Content-Type: text/vcard; text/x-vcard and text/plain are accepted too), or as JSON { "vcf": "BEGIN:VCARD…" }. Add ?dryRun=1 to get the same report without writing anything. In the SDK: faivelo.partner.mailboxes.importContacts(customerId, domain, address, vcfText, { dryRun: true }).

curl -X POST "https://faivelo.com/api/partner/customers/CUSTOMER_ID/domains/example.com/mailboxes/jane%40example.com/contacts/import?dryRun=1" \
  -H "Authorization: Bearer fvl_partner_…" \
  -H "Content-Type: text/vcard" \
  --data-binary @contacts.vcf

The cards go into the mailbox's personal address book (what the person sees under Contacts in webmail), never into the customer's shared directory. The import is add-only: a card whose address (any of them) is already in that book is skipped as exists, and nothing already there is changed or removed. vCard 2.1, 3.0 and 4.0 are read, including folded lines, quoted-printable and UTF-8 names; name (N or FN), every email (the first or PREF one is the primary, up to 10 more are kept), the first phone, organisation, title and note are imported; photos and everything else are ignored. The answer counts what happened:

{ "dryRun": false, "total": 312, "imported": 290, "planned": 290,
  "skipped": { "exists": 14, "duplicate_in_file": 3, "no_email": 5, "invalid": 0 },
  "errors": [{ "index": 7, "reason": "no_email" }], "truncated": false,
  "groupsCreated": 2, "memberships": 41 }

index is the card's position in the file (from 0); errors holds the first 50.

Groups come along too. A card's CATEGORIES line (vCard's comma-separated tags, a literal comma written \,) files the contact into personal groups of those names: a group that does not exist yet is created (names are matched case-insensitively, up to 200 groups per mailbox), and groupsCreated and memberships count what happened. A card skipped as exists changes no group. Roundcube exports a contact's groups as CATEGORIES by itself; if you build the file from its database instead, groups live in contactgroups and contactgroupmembers:

SELECT c.vcard, string_agg(g.name, ',') AS categories
FROM contacts c
LEFT JOIN contactgroupmembers m ON m.contact_id = c.contact_id
LEFT JOIN contactgroups g ON g.contactgroup_id = m.contactgroup_id AND g.del = 0
WHERE c.user_id = :user_id AND c.del = 0
GROUP BY c.contact_id, c.vcard;

and add CATEGORIES:<categories> to each card before sending. Limits: 2 MB of text and 5000 cards per call (413 past the size, truncated: true past the count: split the file), 10 000 personal contacts per mailbox (422 with code: PERSONAL_CONTACTS_CAP before anything is written), 120 imports per 10 minutes per partner. The import is switched on together with migrations (403 MIGRATION_NOT_AVAILABLE until then); an inactive mailbox answers 409. Every real import lands on the customer's audit trail as MAILBOX_CONTACTS_IMPORTED.

Limits

  • 120 requests per minute per key.
  • Provisioning is refused with 403 while your partner account is paused. Adding a domain is refused with 403 PARTNER_DOMAIN_LIMIT at your domain limit, which starts small and grows with clean sending; ask support to raise it sooner.
  • New customers start on the trial sending ceiling and graduate automatically.

Endpoints

The SDK column is the Node method under faivelo.partner; the Python SDK has the same names in snake_case (faivelo.partner.mailboxes.create_webmail_session, faivelo.partner.mail_hosts.add, faivelo.partner.reports.usage_csv).

Customers and domains

MethodPathScopeSDK
GET/customerspartner:readcustomers.list
POST/customerspartner:writecustomers.create
GET/customers/{id}partner:readcustomers.get
DELETE/customers/{id} (sandbox only)partner:writecustomers.delete
POST/customers/{id}/pausepartner:writecustomers.pause
POST/customers/{id}/resumepartner:writecustomers.resume
POST/customers/{id}/plan (every domain)partner:writecustomers.setPlan
GET/customers/{id}/domainspartner:readdomains.list
POST/customers/{id}/domainspartner:writedomains.add
GET/customers/{id}/domains/{domain}partner:readdomains.get
DELETE/customers/{id}/domains/{domain}partner:writedomains.delete
POST/customers/{id}/domains/{domain}/planpartner:writedomains.setPlan
PUT/customers/{id}/domains/{domain}/brandingpartner:writedomains.setBranding
PUT/customers/{id}/domains/{domain}/storagepartner:writedomains.setStorage

Mailboxes

MethodPathScopeSDK
GET/customers/{id}/domains/{domain}/mailboxespartner:readmailboxes.list
POST/customers/{id}/domains/{domain}/mailboxespartner:writemailboxes.create
GET/customers/{id}/domains/{domain}/mailboxes/{address}partner:readmailboxes.get
PATCH/customers/{id}/domains/{domain}/mailboxes/{address}partner:writemailboxes.update
DELETE/customers/{id}/domains/{domain}/mailboxes/{address}partner:writemailboxes.delete
POST/customers/{id}/domains/{domain}/mailboxes/{address}/reset-passwordpartner:writemailboxes.resetPassword
PUT/customers/{id}/domains/{domain}/mailboxes/{address}/storagepartner:writemailboxes.setStorage
POST/customers/{id}/domains/{domain}/mailboxes/{address}/webmail-sessionpartner:writemailboxes.createWebmailSession
POST/customers/{id}/domains/{domain}/mailboxes/{address}/contacts/importpartner:writemailboxes.importContacts

Migrations, keys and outbox

MethodPathScopeSDK
POST/customers/{id}/domains/{domain}/migrations/testpartner:writemigrations.test
POST/customers/{id}/domains/{domain}/migrationspartner:writemigrations.create
GET/customers/{id}/domains/{domain}/migrationspartner:readmigrations.list
GET/customers/{id}/domains/{domain}/migrations/{migrationId}partner:readmigrations.get
POST/customers/{id}/domains/{domain}/migrations/{migrationId}/syncpartner:writemigrations.sync
POST/customers/{id}/domains/{domain}/migrations/{migrationId}/finishpartner:writemigrations.finish
POST/customers/{id}/api-keyspartner:writeapiKeys.create
GET/customers/{id}/api-keyspartner:readapiKeys.list
DELETE/customers/{id}/api-keys/{keyId}partner:writeapiKeys.revoke
GET/outboxpartner:readoutbox.list
GET/outbox/{id}partner:readoutbox.get
GET/reports/usagepartner:readreports.usage, reports.usageCsv

Mail server names

MethodPathScopeSDK
GET/mail-hostspartner:readmailHosts.list
POST/mail-hostspartner:writemailHosts.add
DELETE/mail-hosts/{host}partner:writemailHosts.remove
POST/mail-hosts/{host}/defaultpartner:writemailHosts.setDefault
POST/mail-hosts/{host}/checkpartner:writemailHosts.check
GET/customers/{id}/mail-hostpartner:readmailHosts.forCustomer
PUT/customers/{id}/mail-hostpartner:writemailHosts.setForCustomer
GET/mail-hostpartner:readmailHost.get (default name)
PUT/mail-hostpartner:writemailHost.set (default name)
DELETE/mail-hostpartner:writemailHost.clear (default name)
POST/mail-host/checkpartner:writemailHost.check (default name)

All paths are under https://faivelo.com/api/partner. Full request and response shapes are in the API reference.