Corporate gifting · API v1
Your gifting program, connected.
Bring your employee calendar, client relationships and procurement workflow into Printonic. Start with a test key, quote a gift, then follow it through delivery.
Start in three steps
- Sign in as the team owner. Open Integrations → API keys, acknowledge access, enable the API and create a test key.
- Choose a role and scopes. Use an approver service account for roster, rules and finance access. Save the key when it appears; it is shown once.
- Read
GET /team, browseGET /collections, then quote and create a team order. Keep the returned ID and reuse your idempotency key for retries.
export PRINTONIC_TEAMS_API_KEY='ptk_test_…'
export PRINTONIC_API_BASE='https://staging.printonic.com/api/teams/v1'
curl --fail-with-body "$PRINTONIC_API_BASE/team" \
-H "Authorization: Bearer $PRINTONIC_TEAMS_API_KEY"
curl --fail-with-body "$PRINTONIC_API_BASE/team-orders" \
-H "Authorization: Bearer $PRINTONIC_TEAMS_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: welcome-alex-2026-10-01' \
--data '{"collection":"welcome","headcount":1,"gift_message":"Welcome aboard!"}'Use https://staging.printonic.com/api/teams/v1 while testing this release. The production base after promotion is https://platform.printonic.com/api/teams/v1. Download the OpenAPI specification.
One key, one team
Keys begin with ptk_live_ or ptk_test_. Send them in Authorization: Bearer or X-API-Key. Each service account has a requester or approver role and explicit scopes. Roles never grant owner rights or permission to sign approvals. Key creation and revocation require the owner’s signed-in session.
| Scope | Access |
|---|---|
catalog:read | Read gifts and catalog |
people:read | Read the employee roster |
people:write | Update the employee roster |
contacts:read | Search connected CRM contacts |
gifts:send | Prepare gift sends |
orders:read | Read team orders and approval requests |
orders:write | Prepare and update team orders |
orders:pay | Pay within owner-approved limits |
artwork:write | Manage gift artwork and previews |
rules:write | Manage gifting rules |
billing:read | Read billing and finance documents |
billing:write | Manage billing configuration and funding requests |
webhooks:manage | Manage webhook subscriptions |
The team API switch and the account Agent Access suspension both disable keys. Revocation takes effect on the next authenticated request. Requests have plan-based per-key limits and appear in usage history and the activity feed.
Payments and approvals
Orders and gift sends are held for human approval by default. An owner may grant a live REST key orders:pay with a per-request spending ceiling and a monthly ceiling. A batch must fit within the per-request ceiling as a whole. Reserved and uncertain payments count toward the monthly cap in America/Los_Angeles time. Current prices, proofs, balance and credit terms are checked again before payment.
For a bounded net-terms purchase, pass payment_method: "net_terms" when creating an order, or call POST /funding with {"method":"net_terms","campaign_id":123} for an unpaid order created by the same key. This requires billing:write and orders:pay. Bank funding uses bank_transfer (deposit invoice) or us_bank_account (an ACH intent when enabled). Creating an intent does not confirm a bank payment.
Assistants never approve, fund or pay. MCP tools hold orders even when the credential has a pay scope. Proof signing requires a signed-in approver and the current proof version. API-created rules hold their sends. Large-campaign processing prepares shipments; a human approves payment in Printonic.
Reliable requests and private responses
Use a stable Idempotency-Key of 1–128 printable characters on each endpoint marked below. Reusing it with the same request replays the result; changing the payload returns 409. After an uncertain write, read the resource before trying a new key. Do not generate a fresh key for each network retry.
Lists use limit and an opaque cursor. Follow next_cursor until it is null. Cursors belong to their team, collection and filters. Rate responses include X-RateLimit-*; on 429, honor Retry-After. Every response includes X-Request-Id.
{"error":{"code":"insufficient_scope","message":"This key does not have the required Teams scope.","request_id":"…"}}Responses and exports omit recipient emails, claim URLs and tokens, including nested provider errors. An integration can supply a work email to update a roster or send a gift, then follow the returned IDs. Request bodies cannot choose a different team or owner.
Endpoint reference
All paths are relative to /api/teams/v1. GET/POST /keys and DELETE /keys/{id} require an owner session. POST /proofs/{id}/sign requires an approver session. Every other endpoint uses the key’s listed scopes.
| Method and path | Scopes | Idempotency key |
|---|---|---|
GET /team | Active key | Optional |
GET /members | people:read | Optional |
GET /collections | catalog:read | Optional |
GET /collections/:slug | catalog:read | Optional |
GET /items | catalog:read | Optional |
GET /items/:slug/options | catalog:read | Optional |
GET /people/upcoming | people:read | Optional |
GET /people | people:read | Optional |
POST /people | people:write | Optional |
PATCH /people/:id | people:write | Optional |
DELETE /people/:id | people:write | Optional |
GET /contacts | contacts:read | Optional |
POST /gifts | gifts:send | Required |
GET /gifts | orders:read | Optional |
GET /gifts/:id/timeline | orders:read | Optional |
GET /gifts/:id | orders:read | Optional |
POST /team-orders/quote | catalog:read | Optional |
POST /team-orders | orders:write | Required |
GET /team-orders | orders:read | Optional |
GET /team-orders/:id/manifest.csv | orders:read | Optional |
GET /team-orders/:id/proofs | orders:read | Optional |
GET /team-orders/:id | orders:read | Optional |
POST /team-orders/:id/cancel | orders:write | Required |
PATCH /team-orders/:id | orders:write | Optional |
POST /team-orders/:id/request-approval | orders:write | Required |
GET /approvals | orders:read | Optional |
GET /approvals/:id | orders:read | Optional |
GET /billing/balance | billing:read | Optional |
GET /invoices | billing:read | Optional |
GET /invoices/:id.pdf | billing:read | Optional |
GET /receipts/:id.pdf | billing:read | Optional |
GET /statements/:month | billing:read | Optional |
GET /cost-centers | billing:read | Optional |
GET /credit-terms | billing:read | Optional |
POST /webhooks | webhooks:manage | Optional |
GET /webhooks | webhooks:manage | Optional |
DELETE /webhooks/:id | webhooks:manage | Optional |
POST /webhooks/:id/test | webhooks:manage | Required |
GET /artwork | artwork:write | Optional |
POST /artwork | artwork:write | Required |
POST /artwork/previews | artwork:write | Required |
GET /previews/:id | artwork:write | Optional |
GET /print-areas | catalog:read | Optional |
GET /templates | orders:read | Optional |
POST /templates | orders:write | Required |
GET /templates/:id/use | orders:read | Optional |
GET /templates/:id | orders:read | Optional |
PUT /templates/:id | orders:write | Optional |
DELETE /templates/:id | orders:write | Optional |
GET /rules | rules:write | Optional |
POST /rules | rules:write | Required |
POST /rules/evaluate | rules:write | Required |
GET /rules/:id/preview | rules:write | Optional |
GET /rules/:id | rules:write | Optional |
PATCH /rules/:id | rules:write | Optional |
DELETE /rules/:id | rules:write | Optional |
POST /proofs/:id/versions | artwork:write | Required |
POST /proofs/:id/review | artwork:write | Required |
POST /proofs/:id/sign | Approver session | Required |
POST /large-campaigns | orders:write | Required |
GET /large-campaigns/:id | orders:read | Optional |
PATCH /large-campaigns/:id | orders:write | Required |
POST /large-campaigns/:id/process | orders:write | Required |
GET /store | catalog:read | Optional |
POST /store/quote | catalog:read | Optional |
POST /store/orders | orders:write | Required |
POST /funding | billing:write | Required |
POST /deposit-invoices | billing:write | Required |
POST /tax-certificates | billing:write | Required |
POST /cost-centers | billing:write | Optional |
PUT /billing/profile | billing:write | Optional |
GET /recipient-branding | orders:read | Optional |
PATCH /recipient-branding | orders:write | Optional |
POST /sandbox/events | orders:write | Required |
HR, CRM and programs
POST /people accepts a people array with work_email, first_name, last_name, start_date, birthday (MM-DD), department, location, country, zip and status. Upserts match the work email. Provider-managed fields stay controlled by that provider. Roster and upcoming-date reads require an approver key with people:read.
POST /gifts accepts 1–50 recipients: {person_id:123}, {crm:{provider:"hubspot",id:"1842",object_type:"contact"}}, or a typed name and email. Choose one of gift.collection_slug, gift.item_slug or gift.items. Connected CRM IDs are resolved server-side within the team.
const base = process.env.PRINTONIC_API_BASE;
const response = await fetch(base + '/gifts', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PRINTONIC_TEAMS_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'crm-deal-1842-thank-you-v1'
},
body: JSON.stringify({
recipients: [{ crm: { provider: 'hubspot', id: '1842', object_type: 'contact' } }],
gift: { collection_slug: 'thank-you' },
message: 'Thank you for choosing us.'
})
});
const result = await response.json();
if (!response.ok) throw new Error(result.error.code + ': ' + result.error.message);
console.log(result); // IDs and status; no recipient email or claim linkUse kind=hr or kind=crm for rules. Template use returns a fresh campaign_input with its template revision; add new recipients and submit it to team-orders. Gift-choice menus use gift_choices with bundle mode. Large campaigns accept up to 5,000 recipients. The process endpoint prepares the next batch; action: "stop" stops the program. Use pause and resume to control preparation. Reprice processes one unpaid batch per request; repeat with a fresh Idempotency-Key until the program returns to awaiting_approval. These actions clear prior payment authority, and changed prices require renewed human approval. Paid batches keep their existing totals.
Artwork, proofs and procurement
Upload artwork from a public HTTPS image URL, then submit placements to POST /artwork/previews. Poll GET /previews/{id} for the actual renderer result. Read print areas before placing art. Proof version and review calls operate on the order item ID; the team-order proofs read uses the campaign ID.
Finance reads include balance, credit terms, invoices, receipts, monthly statements and cost centers. Download PDF documents at the listed paths, or add ?format=pdf to a statement. Billing writes require an approver key with billing:write. Tax certificates use a public HTTPS PDF, PNG, JPEG or WebP URL, up to 10 MB.
import json, os, urllib.request, urllib.error
base = os.environ['PRINTONIC_API_BASE']
headers = {'Authorization': 'Bearer ' + os.environ['PRINTONIC_TEAMS_API_KEY']}
cursor = None
while True:
url = base + '/people?limit=100' + ('&cursor=' + cursor if cursor else '')
with urllib.request.urlopen(urllib.request.Request(url, headers=headers)) as r:
page = json.load(r)
for person in page['data']:
print(person['id'], person.get('display_name'))
cursor = page['next_cursor']
if not cursor:
breakSandbox and webhooks
A test key gets an isolated resource namespace within its team. Test records have UUID IDs. Quotes use live catalog prices; selecting a collection may prepare reusable catalog draft products. Test orders never enter production, send recipient emails or move money. Simulated records and webhooks carry livemode: false.
Use POST /sandbox/events with {"type":"team_order.shipped","resource_id":"RETURNED_TEST_ORDER_UUID"} and an Idempotency-Key to advance a simulated order and exercise webhook delivery. Test artwork and proofs model metadata and review states without rendering images. Test CRM automation uses sandbox roster or typed recipients; it does not read connected providers. Live keys use the existing real renderers and CRM integrations.
Create a test subscription with POST /webhooks. Save its signing secret from the creation response. Verify X-Printonic-Signature: the timestamp and raw body are signed as HMAC-SHA256(secret, timestamp + "." + rawBody). Use constant-time comparison, reject stale timestamps and deduplicate event IDs. Delivery retries use the existing webhook queue and eventually dead-letter repeated failures.
Events cover sent/claimed/shipped/delivered gifts, team-order fulfillment, requested/decided approvals, issued/paid invoices and low balances. A balance.low subscription requires balance_threshold_cents and fires on a real downward crossing. Test subscriptions receive events from the same test key only; live subscriptions belong to the team.
Connect assistants and Zapier
Use the same Teams key at the hosted MCP endpoint. The Teams assistant reference describes all available tools. OAuth follows the existing environment’s sign-in configuration.
The repository includes a Zapier integration for gift sends, roster upserts and signed lifecycle events, using these same API calls. It must be registered and published in the Printonic Zapier developer account before it appears in the Zapier directory.