A transfer goes from one Appmint business to another. It is a request: the sending business names what goes and where, and nothing moves until the receiving business accepts it. On accept, the engine copies the records in the background, 200 at a time. It writes progress and the result onto the request, and emails both sides when it ends. The API is appengine org-management/transfer/requests (src/org-management/org-transfer.service.ts).
sender: request (pending) ──notice + email──→ receiver: review
├─ Decline (with a reason) → rejected
└─ Accept → running → completed / partial / failed
sender: Withdraw while pending → cancelled (pending for 7 days, then expired)| State | Means |
|---|---|
pending | Sent, waiting for the receiving business. |
running | Accepted, being copied. Accepting goes straight here. |
completed | Every record arrived. |
partial | Some arrived, some failed. The request lists why. |
failed | None arrived, or the job stopped (error). |
rejected | Declined by the receiving business, with its reason. |
cancelled | Withdrawn by the sender while pending. |
expired | Nobody answered within 7 days. Expiry shows the next time the request is read. |
Copy or move. A copy leaves the originals where they are. A move deletes each original once its copy has landed; the originals go to the sender's trash (Restore from trash). The Studio screen always sends a copy. A move is API-only.
Ask first
- Where to? The receiving business's ID (the id its people sign in with), not its name.
- What goes? Which collections (products, contacts, a custom collection). For each one: all of it, the records matching one filter, or particular rows.
- Copy or move? Default to copy. A move empties those records out of this business.
- Reason and note. The receiving business sees both while it decides.
- Who accepts? Someone at the receiving business has to accept. Ask who to tell.
Steps
1. Studio › Data Transfers: check the business ID
call_appengine GET /org-management/transfer/requests/check-org/<id>. It answers { exists, orgId, name }. Otherwise it gives a reason: "No organization with that id", or "That is this organization" (both tested).
Hands on: the receiving business's ID and name.
Check: exists: true, and the name is the business the person means. Read the name back to them.
2. Studio › Data Transfers: what goes
Collect the scope. One entry per collection:
| Entry | Sends |
|---|---|
{ "datatype": "product" } | Every product. |
{ "datatype": "contact", "filter": { "data.tags": "wholesale" } } | Only matching records. The filter is a find query, the same shape as repo_find's query. |
{ "datatype": "product", "ids": ["<sk>", "<sk>"] } | Only these records. Each must be in this business. |
Count each one first with repo_find (the request refuses a collection or filter with nothing in it: "
Hands on: the scope list and each count.
3. Studio › Data Transfers: send the request
Stop: sending notifies the receiving business by banner and email, and accepting it puts the records into their business. Show the person:
- the receiving business's name and ID;
- each collection with its count;
- copy or move;
- the reason and the note.
Send only after a clear yes.
call_appengine POST /org-management/transfer/requests
{ "targetOrgId": "<their id>", "mode": "copy",
"scope": [ { "datatype": "product" }, { "datatype": "contact", "filter": { "data.tags": "wholesale" } } ],
"reason": "Catalog for the Plano store", "message": "Prices are retail; adjust before publishing." }The answer is the request: requestId, status: "pending", scope with each count, totalRecords, expiresAt (7 days on). The receiving business gets an in-app notice, "Review request", linking to the request. Its admins (or, when it has none, its company email) get the org-transfer-request email.
On screen: Configuration › Data Transfers (/org-transfers) › Send data to another business. Add collections, or the rows ticked in a collection's table. The table also has Send to another organization, which starts a request with the ticked rows. Then enter the Business ID; it is checked as you type. Press Send now. The screen sends all of a collection or the ticked rows, never a filter.
Hands on: the requestId.
Check: GET /org-management/transfer/requests/<requestId> shows pending. The We sent tab lists it.
4. The receiving business: accept or decline
This happens in the receiving business, by one of its admins. When the person you are working for is the receiving business:
GET /org-management/transfer/requests?direction=incoming&status=pendinglists what is waiting.GET …/<requestId>shows the scope, counts, mode, reason and note.- Stop: accepting copies the records into this business at once, and for a move it deletes them from the sender. It can't be undone from the request. Show the person what is in it and whether it is a move, and accept only after a clear yes.
- Accept:
POST /org-management/transfer/requests/<requestId>/accept. The answer comes backrunningstraight away. - Decline:
POST …/<requestId>/rejectwith{ "reason": "…" }. The sender sees the reason and gets an email.
On screen: Configuration › Data Transfers, Coming to us tab: open the request, then Accept (Accept and transfer) or Decline.
The sender can withdraw a pending request: POST …/<requestId>/cancel (Withdraw on screen). After that it can't be accepted.
Check: the request's status and acceptedBy / rejectedBy / cancelledBy.
5. Data Transfers: follow it to the end
GET /org-management/transfer/requests/<requestId> while it runs:
processedRecordsoftotalRecords;- per collection,
successCountandfailCountinscope; results, which has one row per record:assetId,success, thenewIdin the receiving business, or theerror.
At the end it reads completed, partial or failed, with completedAt. Both businesses get the org-transfer-status email.
A record that already exists in the receiving business with the same unique value (a product's SKU, a name) fails. The screen shows that as "already in
Check: in the receiving business, repo_find on each collection with { "data.transferredFrom.orgId": "<sender id>" } returns the arrivals.
What a transferred record keeps
- Its data, under a new id. The copy is a new record with the same
dataand a newsk. It getsdata.transferredFrom{ orgId, originalId, transferredAt, transferredBy, mode }. - Not its links by id. Anything that points at another record by its id (an order's customer, a product's category id) still holds the sender's id. Send related collections together, and check the links after.
- Not its history. Activity, comments and trash stay with the sender.
When it's done
Tell the person:
- the request's final state;
- per collection, how many arrived and how many failed, with the reasons;
- for a move, that the originals are in this business's trash;
- how the receiving business finds the new records (
data.transferredFrom).
Open Configuration › Data Transfers on the request.
What it can't do yet
- Customers, users, reservations and events don't transfer. Every such record fails, because the engine won't create them through a transfer. Staff users, bookings, events and companies fail too. The exception is customers that are only contacts (
accountType: "contact"), which do arrive. Bring real customers over with a sign-up or import in the receiving business (Import records into a collection). - No filter or move on screen. The Studio screen sends whole collections or ticked rows, always as a copy. Filters and moves are API-only.
- No undo. A copy stays in the receiving business until someone deletes it there. A move's originals can be restored from the sender's trash, but the copies stay.
- No accept by email link. The email points to the Data Transfers screen.
- Expiry is lazy. A request past 7 days shows
expiredonly when it is next read. - A job that stops sends no email. When the copy itself breaks (
failedwith anerror), neither business is emailed. Read the request.
Don'ts
- Don't send a request without the person's yes on the business, the counts and the mode.
- Don't accept a request on the receiving side without the person's yes. And never for a move they haven't understood.
- Don't use a move to "try it". Copy first, check, then delete what you meant to remove.
- Don't send customers (other than contacts), users, reservations or events expecting them to arrive.
- Don't resend a whole collection to fill in what failed. Duplicates fail again. Send the failed rows by
idsafter fixing them.