Learn
Recipes — common workflows
The reference documents one endpoint at a time. Real features chain several together. Here are three end-to-end flows — pulled from production-style examples — that show how one call's output feeds the next.
These workflows lean on PNE resources — customers,
products, and the appointment ShowAvailable search — that
the VISH subset doesn't expose. The auth, paging,
ID and date conventions are identical on both APIs, so the shapes below
transfer; just swap in the resources your API actually has. VISH-specific
differences are flagged inline.
The setup these recipes share
Every snippet below assumes you've already built an authenticated client, exactly as
in the PNE quickstart — so we don't repeat auth in each
step. In Python that's a requests.Session that carries your gateway
apikey on every call and sets the Basic Authorization
header:
# Each curl below omits these for brevity — add them to every request:
# ?apikey=YOUR_API_KEY (gateway key, on the query string)
# -u "USERNAME:PASSWORD" (cURL turns this into the Basic header)const base = "https://api.envisiongo.com/api/v1";
const key = "YOUR_API_KEY";
const token = btoa("USERNAME:PASSWORD");
const authHeader = { Authorization: `Basic ${token}` };import base64, requests
base = "https://api.envisiongo.com/api/v1"
s = requests.Session()
s.params = {"apikey": "YOUR_API_KEY"} # sent on every request
s.headers["Authorization"] = "Basic " + base64.b64encode(b"USERNAME:PASSWORD").decode()using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var http = new HttpClient { BaseAddress = new Uri("https://api.envisiongo.com/api/v1/") };
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes("USERNAME:PASSWORD"));
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
// Add apikey to each request URL: "customers?apikey=YOUR_API_KEY"Creating appointments and orders changes real records immediately — there's no sandbox. Run writes against an account you control, with disposable records you can clean up afterward.
Build a customer 360 view
Pull a customer's profile together with their upcoming appointments and their
purchase history — the data behind a "client card" screen. The thread that ties the
calls together is the CustomerId: you look it up once,
then reuse it.
- Find the customer — GET
/customers, filtered. - Read their appointments — POST
/customers/{id}/Appointments. - Read their purchases — POST
/customers/{id}/Purchases.
1 · Look up the customer
Search by any combination of FirstName, LastName,
Email or MobilePhone. Grab the
CustomerId off the first match — it's an
opaque string, so pass it back verbatim.
curl "https://api.envisiongo.com/api/v1/customers?apikey=YOUR_API_KEY&LastName=Client&Rows=1" \
-u "USERNAME:PASSWORD"
# → [ { "CustomerId": "aFBoL3M3...", "CompanyId": "WlBiYmF...", "FirstName": "Test", ... } ]const res = await fetch(`${base}/customers?apikey=${key}&LastName=Client&Rows=1`, { headers: authHeader });
const [customer] = await res.json();
const custId = customer.CustomerId;
const companyId = customer.CompanyId;match = s.get(f"{base}/customers", params={"LastName": "Client", "Rows": 1}).json()
cust_id = match[0]["CustomerId"] # e.g. "aFBoL3M3ODJoSFNhdDNSb3hDK0hNdz09"
company_id = match[0]["CompanyId"] # carry this for scoping later callsvar res = await http.GetAsync("customers?apikey=YOUR_API_KEY&LastName=Client&Rows=1");
var customers = await res.Content.ReadFromJsonAsync<JsonElement[]>();
var custId = customers[0].GetProperty("CustomerId").GetString();
var companyId = customers[0].GetProperty("CompanyId").GetString();2 · Read their appointments
POST
The customer sub-resources — Appointments, Purchases,
CancelledAppointments, GiftCertificates — are fetched with
POST, not GET, even though they only
read. Send an empty body. Optional StartDate/EndDate (plus
Page/Rows) narrow the window.
curl -X POST "https://api.envisiongo.com/api/v1/customers/CUSTOMER_ID/Appointments?apikey=YOUR_API_KEY" \
-u "USERNAME:PASSWORD" \
--data ""
# → [ { "AppointmentId": "...", "StartDate": "2026-05-21T12:00:00",
# "Service": "test service", "Employee": "Andrew Ault", ... } ]const res = await fetch(`${base}/customers/${custId}/Appointments?apikey=${key}&StartDate=2026-06-01&EndDate=2026-06-30`, {
method: "POST", headers: { ...authHeader, "Content-Type": "application/json" }, body: "{}"
});
const appts = await res.json();
appts.forEach(a => console.log(a.StartDate, a.Service));appts = s.post(f"{base}/customers/{cust_id}/Appointments",
params={"StartDate": "2026-06-01", "EndDate": "2026-06-30"}).json()
for a in appts:
print(a["StartDate"], a["Service"], "with", a["Employee"])var res = await http.PostAsync(
$"customers/{custId}/Appointments?apikey=YOUR_API_KEY&StartDate=2026-06-01&EndDate=2026-06-30",
new StringContent("{}", Encoding.UTF8, "application/json"));
var appts = await res.Content.ReadFromJsonAsync<JsonElement[]>();3 · Read their purchase history
Same shape, different sub-resource. Each row carries the human-readable
Item, Employee and Customer names alongside the
IDs, so a history list needs no extra lookups.
curl -X POST "https://api.envisiongo.com/api/v1/customers/CUSTOMER_ID/Purchases?apikey=YOUR_API_KEY" \
-u "USERNAME:PASSWORD" \
--data ""
# → [ { "OrderId": "...", "OrderNumber": "2", "Item": "test service",
# "OrderDate": "2025-09-08T14:04:04", "Qty": 1, "Amount": 100.0 } ]const res = await fetch(`${base}/customers/${custId}/Purchases?apikey=${key}`, {
method: "POST", headers: { ...authHeader, "Content-Type": "application/json" }, body: "{}"
});
const purchases = await res.json();
const total = purchases.reduce((sum, p) => sum + p.Amount, 0);
console.log(`${purchases.length} purchases, $${total.toFixed(2)} lifetime`);purchases = s.post(f"{base}/customers/{cust_id}/Purchases").json()
total_spent = sum(p["Amount"] for p in purchases)
print(f"{len(purchases)} purchases, ${total_spent:,.2f} lifetime")var res = await http.PostAsync(
$"customers/{custId}/Purchases?apikey=YOUR_API_KEY",
new StringContent("{}", Encoding.UTF8, "application/json"));
var purchases = await res.Content.ReadFromJsonAsync<JsonElement[]>();Full field lists: Customers reference →
Book a visit from an open slot
Turn "this customer wants a service with this provider" into a booked appointment. You assemble three IDs — service, employee, company — ask the API which slots are free, then write the appointment into one of them.
- Choose a service — GET
/services(givesServiceId+ its duration). - Choose a provider — GET
/employees(givesEmployeeId). - Find open slots — GET
/appointments/ShowAvailable. - Book one — POST
/appointments.
1 · Pick the service and provider
List services and employees (both scope to a location with
CompanyId). A service's InitialMinutes /
ProcessMinutes / CompleteMinutes become the appointment's
Init / Delay / Complete in step 4.
curl "https://api.envisiongo.com/api/v1/services?apikey=YOUR_API_KEY&CompanyId=COMPANY_ID&IsActive=true" \
-u "USERNAME:PASSWORD"
curl "https://api.envisiongo.com/api/v1/employees?apikey=YOUR_API_KEY&CompanyId=COMPANY_ID&IsActive=true" \
-u "USERNAME:PASSWORD"const svcs = await (await fetch(`${base}/services?apikey=${key}&CompanyId=${companyId}&IsActive=true`, { headers: authHeader })).json();
const emps = await (await fetch(`${base}/employees?apikey=${key}&CompanyId=${companyId}&IsActive=true`, { headers: authHeader })).json();
const serviceId = svcs[0].ServiceId, employeeId = emps[0].EmployeeId;
const duration = svcs[0].InitialMinutes;svc = s.get(f"{base}/services", params={"CompanyId": company_id, "IsActive": True}).json()[0]
emp = s.get(f"{base}/employees", params={"CompanyId": company_id, "IsActive": True}).json()[0]
service_id, employee_id = svc["ServiceId"], emp["EmployeeId"]
duration = svc["InitialMinutes"] # e.g. 15var svcs = await (await http.GetAsync($"services?apikey=YOUR_API_KEY&CompanyId={companyId}&IsActive=true")).Content.ReadFromJsonAsync<JsonElement[]>();
var emps = await (await http.GetAsync($"employees?apikey=YOUR_API_KEY&CompanyId={companyId}&IsActive=true")).Content.ReadFromJsonAsync<JsonElement[]>();
var serviceId = svcs[0].GetProperty("ServiceId").GetString();
var employeeId = emps[0].GetProperty("EmployeeId").GetString();
var duration = svcs[0].GetProperty("InitialMinutes").GetInt32();2 · Find an open slot
ShowAvailable needs all four query params —
CompanyId, EmployeeId, ServiceId and a
StartDate (the day to search). It returns the bookable
AvailableSlot times for that provider/service on that day.
curl "https://api.envisiongo.com/api/v1/appointments/ShowAvailable?apikey=YOUR_API_KEY&CompanyId=COMPANY_ID&EmployeeId=EMPLOYEE_ID&ServiceId=SERVICE_ID&StartDate=2025-09-11T12:00:00" \
-u "USERNAME:PASSWORD"
# → [ { "AvailableSlot": "2025-09-11 13:15:00", "EmployeeName": "Lev Kurts", "Initial": 15, ... }, ... ]const qs = new URLSearchParams({ apikey: key, CompanyId: companyId, EmployeeId: employeeId, ServiceId: serviceId, StartDate: "2025-09-11T12:00:00" });
const slots = await (await fetch(`${base}/appointments/ShowAvailable?${qs}`, { headers: authHeader })).json();
const firstSlot = slots[0].AvailableSlot;slots = s.get(f"{base}/appointments/ShowAvailable", params={
"CompanyId": company_id, "EmployeeId": employee_id,
"ServiceId": service_id, "StartDate": "2025-09-11T12:00:00",
}).json()
first_slot = slots[0]["AvailableSlot"] # e.g. "2025-09-11 13:15:00"var res = await http.GetAsync($"appointments/ShowAvailable?apikey=YOUR_API_KEY&CompanyId={companyId}&EmployeeId={employeeId}&ServiceId={serviceId}&StartDate=2025-09-11T12:00:00");
var slots = await res.Content.ReadFromJsonAsync<JsonElement[]>();
var firstSlot = slots[0].GetProperty("AvailableSlot").GetString();3 · Book the appointment
Now POST /appointments with the IDs you gathered and the slot you chose.
A 200 OK returns the new appointment's ID, e.g.
{"id": "U3V2Z1BZ…"}.
The create body is an appointment — the
Appointment model: CustomerId,
CompanyId, EmployeeId, ServiceId,
StartDate, the Init/Delay/Complete
minutes, Notes and BookedOnline. (Some older examples paste
the customer field list here by mistake — ignore that; use the fields below.)
curl -X POST "https://api.envisiongo.com/api/v1/appointments?apikey=YOUR_API_KEY" \
-u "USERNAME:PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"CompanyId": "COMPANY_ID",
"CustomerId": "CUSTOMER_ID",
"EmployeeId": "EMPLOYEE_ID",
"ServiceId": "SERVICE_ID",
"StartDate": "2025-09-11T13:15:00",
"Init": 15, "Delay": 0, "Complete": 0,
"Notes": "Booked via API",
"BookedOnline": true
}'const res = await fetch(`${base}/appointments?apikey=${key}`, {
method: "POST",
headers: { ...authHeader, "Content-Type": "application/json" },
body: JSON.stringify({
CompanyId: companyId, CustomerId: custId,
EmployeeId: employeeId, ServiceId: serviceId,
StartDate: "2025-09-11T13:15:00",
Init: duration, Delay: 0, Complete: 0,
Notes: "Booked via API", BookedOnline: true,
}),
});
console.log(await res.json()); // {"id": "U3V2Z1BZ..."}booked = s.post(f"{base}/appointments", json={
"CompanyId": company_id,
"CustomerId": cust_id,
"EmployeeId": employee_id,
"ServiceId": service_id,
"StartDate": "2025-09-11T13:15:00", # a slot from step 2
"Init": duration, "Delay": 0, "Complete": 0,
"Notes": "Booked via API",
"BookedOnline": True,
}).json()
print("new appointment:", booked) # {"id": "U3V2Z1BZ..."}var body = JsonSerializer.Serialize(new {
CompanyId = companyId, CustomerId = custId,
EmployeeId = employeeId, ServiceId = serviceId,
StartDate = "2025-09-11T13:15:00",
Init = duration, Delay = 0, Complete = 0,
Notes = "Booked via API", BookedOnline = true,
});
var res = await http.PostAsync(
"appointments?apikey=YOUR_API_KEY",
new StringContent(body, Encoding.UTF8, "application/json"));
Console.WriteLine(await res.Content.ReadAsStringAsync());On VISH, appointments is read-only and there's no
ShowAvailable search, so this booking flow is PNE-only. VISH clients can
still list services, employees and appointments — see the
VISH reference.
Ring up a retail order
Record a sale: find what's being sold, then post an order that bundles its line items and payments in one call. An order is a composite document, so you build the whole thing client-side and submit it together.
- Find the item — GET
/products(or a service from recipe 2). - Create the order — POST
/orderswith line items + payments. - Read it back — GET
/orders.
1 · Find the product
List products for the location. Per-location pricing and stock live in
the nested ProductDetails array (matched by CompanyId), so
read the price from the entry for the company you're selling at.
curl "https://api.envisiongo.com/api/v1/products?apikey=YOUR_API_KEY&CompanyId=COMPANY_ID&IsActive=true" \
-u "USERNAME:PASSWORD"
# → [ { "ProductId": "...", "Description": "Test Product",
# "ProductDetails": [ { "CompanyId": "...", "Price": 20.0, "QtyOnHand": 0 } ] } ]const prods = await (await fetch(`${base}/products?apikey=${key}&CompanyId=${companyId}&IsActive=true`, { headers: authHeader })).json();
const prod = prods[0];
const itemId = prod.ProductId;
const detail = prod.ProductDetails.find(d => d.CompanyId === companyId);
const price = detail.Price;prod = s.get(f"{base}/products", params={"CompanyId": company_id, "IsActive": True}).json()[0]
item_id = prod["ProductId"]
price = next(d["Price"] for d in prod["ProductDetails"] if d["CompanyId"] == company_id)var prods = await (await http.GetAsync($"products?apikey=YOUR_API_KEY&CompanyId={companyId}&IsActive=true")).Content.ReadFromJsonAsync<JsonElement[]>();
var prod = prods[0];
var itemId = prod.GetProperty("ProductId").GetString();
var detail = prod.GetProperty("ProductDetails").EnumerateArray().First(d => d.GetProperty("CompanyId").GetString() == companyId);
var price = detail.GetProperty("Price").GetDouble();2 · Create the order
Each line item declares its kind with InvType, and each payment its
method with a numeric PaymentType. The two enums:
InvType (line item) | PaymentType (payment) |
|---|---|
R Retail · S Service · P Package · O Other · G Gift | 1 Cash · 2 Check · 3 Gift · 4 Visa · 5 MC · 6 AMEX · 7 Discover · 18 Debit |
The order's SubTotal/Total should equal the sum of its line
items, and the Payments should cover the Total. The example
sells one retail item at $20 and pays $20 cash. Most cost/tax fields can be
0.0 when you're just recording a straightforward sale.
curl -X POST "https://api.envisiongo.com/api/v1/orders?apikey=YOUR_API_KEY" \
-u "USERNAME:PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"CompanyId": "COMPANY_ID",
"CustomerId": "CUSTOMER_ID",
"EmployeeId": "EMPLOYEE_ID",
"OrderDate": "2025-09-08T14:04:04",
"SubTotal": 20.0, "Total": 20.0,
"LineItems": [ { "CompanyId": "COMPANY_ID", "ItemId": "PRODUCT_ID",
"InvType": "R", "Qty": 1, "RetailPrice": 20.0, "ExtendedPrice": 20.0 } ],
"Payments": [ { "CompanyId": "COMPANY_ID", "PaymentType": 1,
"Amount": 20.0, "PaymentDate": "2025-09-08T14:04:00" } ]
}'const res = await fetch(`${base}/orders?apikey=${key}`, {
method: "POST",
headers: { ...authHeader, "Content-Type": "application/json" },
body: JSON.stringify({
CompanyId: companyId, CustomerId: custId, EmployeeId: employeeId,
OrderDate: "2025-09-08T14:04:04", SubTotal: price, Total: price,
LineItems: [{ CompanyId: companyId, ItemId: itemId, InvType: "R",
Qty: 1, RetailPrice: price, ExtendedPrice: price }],
Payments: [{ CompanyId: companyId, PaymentType: 1,
Amount: price, PaymentDate: "2025-09-08T14:04:00" }],
}),
});
console.log(await res.json()); // {"id": "aEd0K3Jz..."}order = s.post(f"{base}/orders", json={
"CompanyId": company_id,
"CustomerId": cust_id,
"EmployeeId": employee_id,
"OrderDate": "2025-09-08T14:04:04",
"SubTotal": price, "Total": price,
"LineItems": [{
"CompanyId": company_id, "CustomerId": cust_id, "EmployeeId": employee_id,
"ItemId": item_id, "InvType": "R", # R = Retail
"Qty": 1, "RetailPrice": price, "ExtendedPrice": price,
}],
"Payments": [{
"CompanyId": company_id, "PaymentType": 1, # 1 = Cash
"Amount": price, "PaymentDate": "2025-09-08T14:04:00",
}],
}).json()
print("new order:", order) # {"id": "aEd0K3Jz..."}var body = JsonSerializer.Serialize(new {
CompanyId = companyId, CustomerId = custId, EmployeeId = employeeId,
OrderDate = "2025-09-08T14:04:04", SubTotal = price, Total = price,
LineItems = new[] { new { CompanyId = companyId, ItemId = itemId,
InvType = "R", Qty = 1, RetailPrice = price, ExtendedPrice = price } },
Payments = new[] { new { CompanyId = companyId, PaymentType = 1,
Amount = price, PaymentDate = "2025-09-08T14:04:00" } },
});
var res = await http.PostAsync(
"orders?apikey=YOUR_API_KEY",
new StringContent(body, Encoding.UTF8, "application/json"));
Console.WriteLine(await res.Content.ReadAsStringAsync());3 · Read the order back
Confirm it landed with GET /orders (filter by CustomerId,
CompanyId or a date range), or fetch the one you just created with
GET /orders/{id}. The response echoes the full document — line items,
payments, and any Coupons or Tips.
curl "https://api.envisiongo.com/api/v1/orders?apikey=YOUR_API_KEY&CustomerId=CUSTOMER_ID" \
-u "USERNAME:PASSWORD"const res = await fetch(`${base}/orders?apikey=${key}&CustomerId=${custId}`, { headers: authHeader });
const orders = await res.json();
console.log(orders[0].OrderNumber, orders[0].Total);orders = s.get(f"{base}/orders", params={"CustomerId": cust_id}).json()
print(orders[0]["OrderNumber"], orders[0]["Total"])var res = await http.GetAsync($"orders?apikey=YOUR_API_KEY&CustomerId={custId}");
var orders = await res.Content.ReadFromJsonAsync<JsonElement[]>();
Console.WriteLine($"{orders[0].GetProperty("OrderNumber")} {orders[0].GetProperty("Total")}");products is PNE-only, so this retail flow is written for PNE. VISH
exposes orders and services — check the
VISH reference for exactly what its order surface
supports before adapting this.
All three lean on the same conventions: opaque IDs you pass back verbatim, paging on the list calls, and status-code-first error handling. Learn those once in Guides & concepts and every recipe reads the same way.