v0 + Vercel

Add AppClickPrint to a v0 app

Use Next.js Route Handlers or Vercel Functions and sensitive environment variables. Never mark an AppClickPrint credential with NEXT_PUBLIC_.

Give this exact instruction to v0 + Vercel.
Read https://appclickprint.com/integrations/v0.html and https://appclickprint.com/developers.html completely before editing.

Use Next.js Route Handlers under app/api or another Vercel Function runtime. Preserve this project's existing authentication and tenant model. Implement provisioning, setup-status polling, printer retrieval and selection, print submission, stable job-ID storage, and terminal webhooks if the UI shows final outcomes. Add two permanent customer controls: Add Gateway calls the authenticated host backend, which requests POST /v1/developer/customers/{externalId}/gateway-code and returns only the temporary setupCode, qrCodeDataUrl, expiresAt, remainingUses and playStoreUrl for display; Manage Gateways requests a fresh mode: "gateways" portal session and opens its one-use URL on every click. Show Google Play, QR-scanning and manual-code instructions beside Add Gateway. Record gateway_count before showing the code and detect success when it increases, including when setup_status was already complete. Refresh printers after registration or management and require reselection if the selected printer belonged to a removed gateway. Never expose acm_, acp_ or webhook secrets to frontend code or responses, and never put them in the QR code. Treat HTTP 202 as queued, not printed. Do not skip tests.

Use the platform’s real server boundary

Responsibilityv0 + Vercel implementation
AppClickPrint API callsNext.js Route Handlers under app/api or another Vercel Function runtime
Management keyVercel Project Settings → Environment Variables; use APPCLICKPRINT_MANAGEMENT_KEY without a public prefix and enable it for each required environment
Customer print keysthe project database with encrypted customer keys and server-only data access
Frontend boundaryClient Components call same-origin authenticated Route Handlers. Read the user session and tenant on the server; do not trust externalId from the request body.
Deployment checkEnvironment-variable changes apply only to new deployments. Configure production and preview separately, redeploy, then register each exact Vercel origin used for selection or management.
Two different backend-only keys: acm_ is the one-per-developer management key used for customers, onboarding, status, printers and webhooks. Each customer’s acp_ key is used only to submit that customer’s print jobs. Neither key may appear in the customer-facing frontend.

Required end-to-end flow—do not stop early

  1. Create one AppClickPrint developer account and store the returned acm_ key in v0 + Vercel’s server secret store.
  2. When a tenant enables printing, provision one AppClickPrint customer using the host app’s immutable tenant ID as externalId.
  3. Encrypt and store the returned one-time acp_ key against that tenant. Return no key to the frontend.
  4. Add a permanent Add Gateway button. The frontend calls an authenticated v0 + Vercel backend function; that function requests POST …/gateway-code.
  5. Show the returned QR image, manual setup code, expiry and Google Play link with clear Android gateway instructions.
  6. Record the customer’s current gateway_count, then poll customer status every 2–5 seconds while the Add Gateway panel is open.
  7. When gateway_count increases, retrieve printers and let the user choose an available printer.
  8. Store both immutable printer id and current routing alias on the existing tenant/location/settings record.
  9. Add a permanent Manage Gateways button. On every click, request a fresh portal session with mode: "gateways" so the customer can rename, rediscover, replace or remove gateways and configure printers.
  10. After gateway management, retrieve printers again and require reselection if the selected printer is no longer available or belonged to a removed gateway.
  11. Submit jobs from the backend with the tenant’s acp_ key, current alias and stable idempotency key.
  12. Persist the returned job id. HTTP 202 means queued.
  13. If the product promises final status, receive and verify job.completed/job.failed webhooks.

Provision once; resume without reprovisioning

POST https://appclickprint.com/v1/developer/customers
Authorization: Bearer acm_YOUR_MANAGEMENT_KEY
Idempotency-Key: provision:{stableTenantId}:v1
Content-Type: application/json

{"externalId":"{stableTenantId}","businessName":"Example Company"}

On HTTP 201, save apiKey before returning a safe response such as { created, customerId }. The browser must not receive the raw AppClickPrint response because it contains apiKey. Repeating an existing externalId returns created: false without the existing key; losing it requires deliberate key rotation.

Implement Add Gateway in v0 + Vercel

Keep Add Gateway available before and after initial setup. The authenticated frontend invokes a tenant-scoped v0 + Vercel backend function; only that function uses the acm_ key:

POST https://appclickprint.com/v1/developer/customers/{externalId}/gateway-code
Authorization: Bearer acm_YOUR_MANAGEMENT_KEY
Content-Type: application/json
HTTP/1.1 201 Created
{
  "setupCode": "ABCD-2345",
  "qrCodeDataUrl": "data:image/png;base64,iVBORw0KGgo…",
  "expiresAt": "2026-08-15T04:00:00.000Z",
  "remainingUses": 3,
  "playStoreUrl": "https://play.google.com/store/apps/details?id=com.appclickprint"
}

The backend may return only those temporary display fields to the authenticated intended customer. Render qrCodeDataUrl as the preferred setup option and show setupCode as a manual fallback. The QR payload is exactly the temporary setup code—never an acm_ key, acp_ key, external customer ID or host-app login token. Request a new code on every Add Gateway click; it expires after 30 minutes and can register up to three gateways.

Required instructions beside the code

  1. Install AppClickPrint Gateway from the returned Google Play link on an Android device that will remain near the printers.
  2. Open the app and enter a meaningful gateway name such as “Reception”.
  3. Tap Scan QR code and scan the displayed QR, or choose the manual option and enter the setup code.
  4. Allow discovery and follow the app’s battery/background-running recommendations.
GET /v1/developer/customers/{externalId}
Authorization: Bearer acm_YOUR_MANAGEMENT_KEY

setup_status:
- waiting_for_gateway
- discovering_printers
- complete

complete means a gateway is registered and at least one printer was discovered. It does not mean a test print succeeded. Before generating a code, record gateway_count. Stop polling when it increases, the user cancels or the UI times out. This count comparison is required for additional gateways because setup_status may already be complete.

Keep Manage Gateways available after setup

One AppClickPrint customer can register multiple Android gateways—for printers in different areas, device replacement or resilience. Do not hide management after the first gateway or printer is configured. On every Manage Gateways click, the authenticated frontend asks the v0 + Vercel backend to create this session:

POST https://appclickprint.com/v1/developer/customers/{externalId}/portal-session
Authorization: Bearer acm_YOUR_MANAGEMENT_KEY
Content-Type: application/json

{"mode":"gateways"}

Return only the temporary url and expiresAt to the frontend, then open the URL in a new tab with noopener. The tenant administration page lets the customer add, rename, rediscover, replace or remove gateways, configure printers and send test pages. The URL is short-lived and one-use: create a new session on every click, never provision another customer, and never rotate the acp_ key merely to manage a gateway.

Retrieve, select and refresh printers

GET /v1/developer/customers/{externalId}/printers
Authorization: Bearer acm_YOUR_MANAGEMENT_KEY

Show only printers returned for the authenticated tenant and normally require available: true. Store id for identity and alias for routing. Aliases can be edited, and adding or replacing a gateway can create new printer IDs and aliases. Refresh the list after setup, whenever the customer returns from gateway management, and when a saved alias is rejected. If the selected printer belonged to a removed gateway, require the user to select an available printer again. available reflects the latest discovery report; it is not proof of real-time reachability.

For an AppClickPrint-hosted picker, register the exact v0 + Vercel production/preview origin, create mode: "select" with that origin, and accept appclickprint.printer.selected only when event.origin === "https://appclickprint.com".

Required customer-facing printing controls

Do not implement only the backend. Add these controls to the existing tenant, location or feature-settings screen in v0 + Vercel:

ControlRequired behavior
Add GatewayKeep this visible before and after setup. Ask the authenticated backend for a fresh gateway-code, then show its QR image, manual code, expiry, Google Play link and instructions. Record gateway_count first and refresh printers when it increases.
Manage GatewaysKeep this visible before and after setup. Ask the backend for a fresh mode: "gateways" session on every click and open the temporary tenant-admin URL. Refresh printers when the user returns.
Select printerAsk the backend for mode: "select" with the exact allow-listed app origin, then open the returned one-use URL in a popup or new tab.
Selected printerDisplay the saved name as a snapshot plus a status such as Available or Unavailable from the latest backend refresh. Provide Change printer, and require reselection when its gateway was removed.
PrintDisable until a valid printer ID and current alias are selected. If submission says the printer is unavailable/not found, keep the preference visible and offer Reselect printer.

Add Gateway button flow

// The v0 + Vercel frontend calls its authenticated backend.
// The backend derives externalId from the signed-in tenant and uses acm_.
async function addGateway() {
  const before = await hostBackend.getAppClickPrintCustomerStatus();
  const setup = await hostBackend.createGatewayCode();
  showGatewaySetup({
    qrImageSrc: setup.qrCodeDataUrl,
    manualCode: setup.setupCode,
    expiresAt: setup.expiresAt,
    playStoreUrl: setup.playStoreUrl
  });

  // Poll through the backend every 2–5 seconds while this panel is open.
  // Success is a count increase, even if setup_status was already complete.
  await waitUntilGatewayCountExceeds(Number(before.gateway_count));
  await refreshPrinters();
}

Manage Gateways button flow

// The frontend calls your authenticated v0 + Vercel backend; only the backend uses acm_.
async function manageGateways() {
  const { url } = await hostBackend.createPortalSession({ mode: "gateways" });
  window.open(url, "_blank", "noopener");
  // When the user returns, fetch printers again before displaying or printing.
}

Select printer button flow

// Browser/mobile web UI calls your authenticated v0 + Vercel backend.
async function selectPrinter() {
  const { url } = await hostBackend.createPortalSession({ mode: "select" });
  window.open(url, "appclickprint-select", "width=1100,height=760");
}

window.addEventListener("message", async (event) => {
  if (event.origin !== "https://appclickprint.com") return;
  if (event.data?.type !== "appclickprint.printer.selected") return;

  // Send only the selection to your authenticated backend.
  // The backend re-fetches this tenant's printer list before saving it.
  await hostBackend.savePrinterSelection({
    printerId: event.data.printer.id,
    printerAlias: event.data.printer.alias,
    printerName: event.data.printer.name
  });
});

The selection popup must retain its opener so AppClickPrint can return the selection with postMessage; do not add noopener to this specific popup. If the platform forces noopener, close the picker manually and refresh printers from the backend instead.

Client Components call same-origin authenticated Route Handlers. Read the user session and tenant on the server; do not trust externalId from the request body.

Validate the selection server-side. Do not trust a crafted postMessage or arbitrary alias from the browser. The backend must call the customer printer-list endpoint and confirm that the submitted ID and alias belong to the authenticated tenant before saving them.

Print dimensions belong to the document/template

For PDF jobs, store physical dimensions with the host app’s label, receipt or document template—for example 62 × 29 mm for a badge. Generate the PDF page at that exact size and have the backend send the same values as widthMm=62 and heightMm=29. Do not guess dimensions from pixels, the browser viewport, the printer name or a frontend preview.

Saved print template
- widthMm: 62
- heightMm: 29
- orientation: landscape

Generated PDF page: 62 mm × 29 mm
Submitted job:       widthMm=62, heightMm=29

A PDF whose page is 29 × 62 mm is also accepted as the rotated orientation of 62 × 29 mm. ZPL and ESC/POS jobs use printer-native commands and omit widthMm/heightMm. Copies must be between 1 and 100.

Submit from the backend

const form = new FormData();
form.set("format", "pdf");
form.set("documentUrl", signedPdfUrl);
form.set("filename", "label.pdf");
form.set("widthMm", "62");
form.set("heightMm", "29");
form.set("copies", "1");
form.set("photoMode", "true"); // Optional per-job override; omit to use the saved printer setting.
form.set("idempotencyKey", `host-job:${businessJobId}:v1`);

const response = await fetch(
  `https://appclickprint.com/v1/printers/${encodeURIComponent(alias)}/print`,
  { method: "POST", headers: { Authorization: `Bearer ${customerPrintKey}` }, body: form }
);
const job = await response.json();
if (!response.ok) throw new Error(job.message || job.error);
await saveAppClickPrintJobId(businessJobId, job.id);
return { jobId: job.id, status: job.status }; // queued, not printed

For PDF, the page size must match the supplied millimetres. Optional Boolean photoMode overrides the saved printer setting for that job: true improves photographs using compatible raster drivers, false favours crisp text and barcodes, and omission inherits the saved setting. Native IPP/AirPrint and operating-system drivers process images themselves. Raw ZPL and ESC/POS jobs do not accept photoMode. ZPL and ESC/POS are also supported when the selected driver matches. If a gateway is temporarily offline but the printer remains available, an accepted job queues for reconnect; ensure a signed documentUrl remains valid long enough.

Use accurate outcomes and safe retries

v0 + Vercel release checklist

  1. Search source, generated bundles, browser responses and logs for acm_, acp_ and whsec_; none may be exposed.
  2. Prove one signed-in tenant cannot provision, create gateway codes, inspect printers or print as another tenant.
  3. Retry provisioning and confirm only one AppClickPrint customer exists.
  4. Click Add Gateway twice and confirm each attempt returns fresh temporary display details for the same customer without rotating its acp_ key.
  5. Confirm the QR image and manual code are both usable, expiry and Google Play instructions are visible, and no permanent credential is encoded in the QR.
  6. Register the first gateway and confirm gateway_count increases, printers refresh and the user can select an available printer.
  7. Register a second gateway while setup_status is already complete; confirm the count-based detector succeeds without creating another customer.
  8. Open Manage Gateways twice and confirm each click creates a fresh one-use mode: "gateways" URL.
  9. Remove a gateway through Manage Gateways, refresh printers and confirm a printer on that gateway must be reselected.
  10. Submit the same print idempotency key twice and confirm the same AppClickPrint job ID is returned.
  11. Take the gateway offline, confirm the UI says queued rather than printed, then reconnect it.
  12. If final status is shown, validate webhook HMAC using the raw body and deduplicate by event ID.
  13. Environment-variable changes apply only to new deployments. Configure production and preview separately, redeploy, then register each exact Vercel origin used for selection or management.
Need exact response fields and edge cases? The canonical developer guide is authoritative. This page only maps that lifecycle onto v0 + Vercel.

Platform approach checked against official Vercel Functions and server runtimes.

Canonical developer guide OpenAPI specification