Book a demo

Photo Lab Software · the lab side of volume school photography

The ordering port and workflow layer built for photographic labs — order intake, the PSPA package world, and vendor routing behind one interface.

Labs run software too. The platform’s lab-integration layer — @homeroom/lab-routing — is a single lab-agnostic port that reads what a lab declares it can produce BEFORE it quotes or submits anything. An undeclared capability is denied. A retried submit reconciles to the same lab order instead of creating a duplicate. Ten vendor adapters behind one port cover three distinct lab dialects: the single-recipient REST family, the PSPA volume/SFTP family, and the Richmond XML dialect. The PSPA pony-sort package world — the per-subject sorted crate that goes to a school rather than a parent’s porch — is modeled as a first-class workflow, distinct from dropship. Live submits per vendor are early access, gated on credentials and a test order being in place. We say that rather than presenting the whole port as a live money-moving service.

This is the PHOTOGRAPHIC and volume-lab side: order intake, the PSPA/pony-sort package world, and the routing and workflow layer between the platform and the labs that fulfill portrait orders. It is not the press/print-production or capability-modeling layer (that is printlab.software) and not the yearbook-production toolchain (that is yearbooklab.software). No partner or licensing offer appears on this page — that lane is founder-parked. The product is the port, the adapters, and the early-access lab-facing tools.

What is built and what is in early access

The capabilities below are the core of the lab-routing layer. The first five are built and running today in the platform. Live submits per vendor are early access, gated on credentials being in place. We mark each honestly.

Capabilities before quote — fail-closed by design

The router calls capabilities() on the adapter BEFORE it calls quote() or submit(). A CapabilitySet is the adapter’s owned catalog: the routing modes it serves (dropship to a home address, bulk pony-sorted crate to a school), the SKU classes it can print, the product codes it recognizes, the print sizes it accepts, and the color spaces it takes. If the job requires a capability the lab has not declared — a routing mode it does not serve, a SKU class it does not print, a product code not in its catalog — the router denies the job before any network call is made. An undeclared capability is always a “no”, never a silent pass-through. impl: packages/lab-routing/src/port.ts, CapabilitySet. Shipped

Idempotent submits keyed on a token we own

Every submit() keys on an idempotency token the platform owns — entry_reference, stored in photo_fulfillment_job.entry_reference. The router builds this token once (buildEntryReference); every adapter maps it to the lab’s own idempotency mechanism. A network timeout or a retry loop does not create a second lab order — the lab sees the same entry_reference and reconciles the submit to the existing order. The spine — MAX_SUBMIT_ATTEMPTS, submitBackoffMs, decideSubmit, isReroutable — is built in packages/lab-routing/src/idempotency.ts. A submit that has already committed on one vendor is not re-sent to a different vendor on a retry; rerouting only happens for states the router identifies as reroutable. Shipped

Ten adapters, three lab dialects, one port

Ten adapters in packages/lab-routing/src/adapters implement the same Lab interface: bay, cloudprinter, finerworks, gelato, generic-dropship, prodigi, rpi, rpl-xml, volume, whcc. They span three lab dialects. The DropshipLab family (whcc, bay, prodigi, gelato, cloudprinter, finerworks, rpi) speaks synchronous REST and ships a single rendered flat to a single recipient’s address — Rail A. The VolumeLab family (the volume adapter, covering richmond, hh, fullcolor, aci, jd) builds a PSPA Index.txt manifest and transfers it over SFTP for per-subject pony-sorted fulfillment — Rail B. The RPL XML dialect (rpl-xml) emits and validates a conformant RPL v1.0 structured XML order. All three normalize to the same result vocabulary: LabOrderRef, ProductionStatus, Shipment. Shipped

The PSPA / pony-sort package world

Volume school photography is not a dropship business. A lab that fulfills a school’s portrait package order does not ship one package to one address — it produces a pony-sorted crate where every student’s package is organized by teacher and homeroom so the school can distribute it in one pass. The port models this as a first-class workflow. CapabilitySet carries the load-bearing pony_sort flag: a VolumeLab hard-sets it true; a DropshipLab hard-sets it false. No REST dropship adapter can physically sort a crate, so a bulk-to-school job routed to a DropshipLab is denied at the routing layer, not discovered at fulfillment time. The pony-sort roster carries PonySortRow subjects — studentId, lastName, firstName, grade, teacher, homeRoom — in stable PSPA order (teacher, grade, last, first, studentId), and the PSPA manifest builder produces a conformant Index.txt from that roster. impl: packages/lab-routing/src/volume/pspa-manifest.ts. Shipped

Normalized production vocabulary across every adapter

A lab order goes through a known status progression regardless of which vendor fulfills it. The port normalizes that progression to one vocabulary: Imported, Confirmed, InProduction, Shipped, Delivered, Error, Unknown. Every adapter’s status() method returns a ProductionStatus from this set; every adapter’s verifyWebhook() method normalizes the vendor’s native event shape to a LabWebhookEvent carrying the same kind field. The platform tracks a WHCC order and a VolumeLab order through the same fulfillment ladder (FULFILLMENT_STATUS_RANK, canAdvanceFulfillment) without branching on the vendor. The Quote object carries a capabilityOk assertion and a quotedAt freshness timestamp so stale quotes can be identified and refreshed before they reach submit. Shipped

Lab-facing tooling and live submits

The live submit path per vendor is gated off until that lab’s credentials and a test order are confirmed in place. The RPL adapter is the clearest example: the RPL XML build and validation pipeline is complete, but an RplTransportDisabledError is thrown on any live submit attempt until RPL credentials are configured. The same pattern applies across adapters: the port and the adapter code are built; the credential gate is the live-path condition. This is presented honestly as early-access lab-integration and lab-facing tools. No live money movement is claimed for any vendor today. When a vendor’s credentials are in place and a test order passes, that adapter’s live submit path is enabled. Early access

How the ordering port works

A portrait order on the platform follows a defined path from the platform to the lab. The lab-routing layer sits between the platform’s order engine and every lab adapter. Here is what happens at each step:

  1. The router reads the job. A FulfillmentJob carries everything the adapter needs: the school and order identifiers, the SKU class, the routing mode (dropship to a home address or bulk pony-sorted to the school), the rendered flat assets with their product codes and sizes, the destination address or pony-sort roster, and the two gate flags — consentCleared (the commerce-purpose consent decision was resolved and allowed for this likeness) and commerceEligible (the photo passed QC). The router fail-closes on either flag being false. No likeness ships without a consent grant.
  2. The router selects a lab candidate. The routing-rules engine (R1–R8 decision table) maps the job’s SKU class, routing mode, and destination country to the set of candidate adapters. The ranker scores the candidates by cost and SLA after the capability check; the highest-ranking candidate that declares the job producible wins.
  3. Capabilities are checked before anything else. The router calls capabilities() on the winning adapter first. The CapabilitySet is pure and synchronous — no network call. If the job requires a routing mode, SKU class, or product code the adapter has not declared, the router denies the job immediately. A capability miss does not proceed to quote or submit.
  4. The adapter quotes the job. quote() calls the lab’s API (or builds the manifest) and returns a Quote: landed cost in cents, estimated ship days, a qualified flag, and a quotedAt timestamp. The cost model feeds the ranker’s cost-SLA scoring for the nightly sweep. A stale quote (quotedAt too old) is refreshed before submit.
  5. The adapter submits idempotently. submit() carries the FulfillmentJob and the entry_reference token. The adapter maps that token to the lab’s own idempotency mechanism (WHCC uses a two-phase import + confirm; the VolumeLab uses an SFTP transfer with a _READY sentinel; the generic REST adapters carry the token in a vendor-specific idempotency header). A retry with the same entry_reference reconciles to the existing lab order. submit() returns a LabOrderRef: the lab’s native order ID, the entry_reference echoed back, and the WHCC confirmation token or the VolumeLab manifest key when applicable. The LabOrderRef is written once to the route row and never overwritten.
  6. The route watches production status. When a lab’s webhook arrives, the adapter’s verifyWebhook() verifies the signature and normalizes the event to a LabWebhookEvent. The webhook-apply logic (decideApply) maps the event to a fulfillment-ladder advancement and a Shipment record when the status is Shipped. The poll fallback — status() on the adapter — provides the same ProductionStatus without a webhook, for vendors whose webhooks are delayed or unavailable.
  7. Tracking flows to the order. tracking() returns a Shipment array: carrier, tracking number, shippedAt, and whether the shipment went to the home address (Rail A) or to the school (Rail B). The platform uses the same Shipment structure regardless of adapter, so fulfillment display in the order record is uniform across vendors.

Rail A and Rail B — two fulfillment models, one port

School portrait fulfillment splits into two distinct physical models. The port models both as first-class, and the routing-rules engine routes each job to the correct rail based on the job’s routing mode and the adapter’s declared capabilities.

Rail A — dropship to a home address

A Rail A order ships a single rendered flat to a single recipient’s address — a parent’s porch. The DropshipLab adapter family handles Rail A: WHCC, Bay, Prodigi, Gelato, Cloudprinter, FinerWorks, and RPI. Each speaks a synchronous REST API; the adapter posts the rendered asset URL and the ShipTo address to the lab’s order endpoint and gets back a native order ID. Every DropshipLab hard-sets pony_sort=false in its CapabilitySet. A job with routing mode bulk_to_school is denied at the capability check before any network call, because no dropship REST adapter physically sorts a pony crate. Rail A is the picture-print and premium wall-art pathway: a parent orders a specific print size and finish, and it arrives at their address directly from the lab.

Shipped

Rail B — pony-sorted bulk crate to the school

A Rail B order produces a sorted crate for the school. The VolumeLab adapter family handles Rail B (richmond, hh, fullcolor, aci, jd). The adapter builds a PSPA Index.txt manifest from the job’s pony-sort roster — a list of PonySortRow entries in stable PSPA order: teacher, grade, last name, first name, studentId — and transfers the manifest and the rendered flat assets over SFTP. The lab produces each student’s package and physically sorts the crate by teacher and homeroom so the school can distribute it by classroom in a single pass. Every VolumeLab hard-sets pony_sort=true in its CapabilitySet. A job with routing mode dropship_home cannot be routed to a VolumeLab — the capability check denies it. Rail B is the package-sheet pathway: every student in a grade-level run gets a portrait package sorted by homeroom.

Shipped

The adapters behind the port

Each adapter implements the same Lab interface and is interchangeable from the router’s perspective. The router does not know which adapter it is calling beyond the vendor identifier and the CapabilitySet it reads. The adapters below are the ten built implementations in packages/lab-routing/src/adapters.

WHCC DropshipLab (Rail A)

WHCC uses a bespoke two-phase protocol: an import step that stages the order and returns an import token, then a confirm step that commits the print run. The confirmationId field on the LabOrderRef holds the import token until confirm. Cancel is safe before confirm; after confirm the order is in production. WHCC also supports white-label dropship (dropshipBranded=true in the CapabilitySet) and echoes the asset md5 (WHCC ImageHash) back for integrity verification. The adapter normalizes WHCC’s native status strings to the platform’s ProductionStatus vocabulary and verifies WHCC’s webhook signature scheme before normalizing the event.

Prodigi DropshipLab (Rail A)

Prodigi is the REST dropship adapter for international-capable print products (intl=true). The prodigi adapter and profile are built from the generic DropshipLab infrastructure, with Prodigi-specific SKU code mapping and the md5Hash field carried on the asset submission. Prodigi’s native order statuses are normalized to the platform vocabulary on status() and webhook.

Gelato DropshipLab (Rail A, international)

Gelato is the non-US-capable (intl=true) DropshipLab adapter for premium prints and specialty products. It is the R4 routing target for non-US destination countries in the R1/R2 premium/standard print routing rules. The gelato profile and adapter are built from the generic DropshipLab infrastructure.

Bay DropshipLab (Rail A)

Bay Photo Lab is a DropshipLab REST adapter. It supports white-label dropship (drop_ship = true in the Bay API). The bay profile and adapter are built from the generic DropshipLab infrastructure, with Bay-specific routing and product-code mapping.

Cloudprinter DropshipLab (Rail A)

Cloudprinter is a DropshipLab REST adapter for commodity and poster products. The cloudprinter profile and adapter are built from the generic DropshipLab infrastructure, with Cloudprinter-specific routing and payload construction.

FinerWorks DropshipLab (Rail A, dual-header auth)

FinerWorks is a self-serve REST DropshipLab for fine-art, framed, canvas, metal, and card products. It uses dual-header auth (API key + account key). Status is poll-only: FinerWorks does not provide outbound webhooks, so the fail-closed webhook verifier throws on any incoming webhook attempt (a webhook for this adapter is a misconfiguration, not an event to process). The adapter normalizes FinerWorks status codes to the platform vocabulary on each status() poll.

RPI Print DropshipLab (Rail A, sandbox host)

RPI Print is a self-serve REST DropshipLab for photo books, calendars, and cards — yearbook-adjacent press products. RPI’s API has a sandbox host for test orders. The adapter provides a per-order webhook with signature verification and a polling fallback. normalizeRpiStatus maps RPI’s native status to the platform vocabulary.

Volume (VolumeLab) VolumeLab (Rail B, PSPA/SFTP)

The volume adapter is the PSPA / Rail B handler covering five volume labs (richmond, hh, fullcolor, aci, jd). It builds a PSPA Index.txt manifest from the job’s PonySortRow roster and transfers the manifest and rendered assets over SFTP. The _READY sentinel file signals to the lab that the manifest is complete and ready for production. The VolumeLab hard-sets pony_sort=true and routing_modes=[bulk_to_school, bulk_to_studio]. It never serves a dropship_home job. Each volume vendor is a distinct VolumeLabConfig with its own SFTP endpoint and product-code mapping.

RPL XML XML dialect (honest-off transport)

The RPL (Richmond Pro Lab) adapter emits and validates a conformant RPL v1.0 structured XML order from a FulfillmentJob. It uses an OfferingID + Quantity item model (ProductID / Price deprecated per the v1.0 spec). The XML build and validation pipeline is complete: buildRplOrder constructs the typed RplOrder object from a FulfillmentJob; renderRplXml serializes it to conformant XML; emitRplOrderXml combines both and hands the result to the RplTransport. The live transport is gated off (RplTransportDisabledError) until RPL credentials and a test order are confirmed in place. The adapter implements the full Lab port so the router can test-route to it without a live network dependency.

The PSPA package world — how volume portrait fulfillment actually works

A school portrait package is not a single print shipped to one address. It is a set of prints — a sheet layout carrying multiple sizes and poses — produced for every participating student in a grade-level or school-wide run, sorted by teacher and homeroom, and delivered to the school as a single crate. The lab opens the crate, finds each student’s package by the classroom label, and the school distributes it by classroom in a single pass. This is the PSPA model: the Photo Specialty Products Association (PSPA) established the Index.txt manifest format that most volume portrait labs read, and the platform builds conformant PSPA manifests from the pony-sort roster.

The load-bearing flag in the CapabilitySet is pony_sort. A VolumeLab hard-sets it true: it is physically capable of accepting a PSPA manifest and producing a sorted crate. A DropshipLab hard-sets it false: it accepts one rendered flat and ships to one address. The routing layer uses this flag as a structural constraint. A bulk-to-school job with a pony-sort roster cannot be routed to a DropshipLab regardless of the rest of its capabilities. The master-12a section 3.1 finding — that no single-recipient REST adapter can physically sort a crate — is enforced in code, not in documentation.

The PonySortRow carries what the lab needs to sort: studentId, lastName, firstName, and optionally grade, teacher, and homeRoom. No biometric data and no face-match data leave the platform on a volume manifest. The sort fields are the roster grouping keys the lab needs to label and sort the crate. The PSPA manifest builder (buildPspaManifest in packages/lab-routing/src/volume/pspa-manifest.ts) produces the conformant Index.txt row order — teacher, grade, last, first, studentId — and associates each row with its rendered asset filename (imageFilenameFor). The adapter drops both the Index.txt and the asset files onto the SFTP endpoint, then writes the _READY sentinel to signal manifest completion.

The platform models the pony-sort roster as part of the FulfillmentJob itself (ponySort: PonySortRow[]), not as a separate file the platform assembles outside the routing layer. The router passes the roster to the adapter; the adapter builds the manifest. The manifest is never built by the router directly, which keeps the VolumeLab implementation details behind the port boundary.

What this is not — and where to find those products

The platform covers several distinct lab and production concerns. Photo Lab Software is specifically the photographic and volume-lab side. The products below are related but distinct:

printlab.software is the press/print-production side: the per-vendor capability profiles (@homeroom/print-delivery), producibility gating for yearbooks and publications, and print-ready preflight. It models what a press plant (not a photographic lab) can produce — binding styles, paper stocks, lamination, tip-ins, cover treatments, and publication types. The CapabilityProfile in @homeroom/print-delivery is the complete closed-world capability model for press vendors. Photo Lab Software is the separate layer for photographic lab orders. The two packages are distinct codebases with distinct port shapes; neither calls the other.

yearbooklab.software is the yearbook-production toolchain: the composition-to-print pipeline that produces a print-ready yearbook PDF and hands it off to a capable press vendor. It uses @homeroom/print-delivery’s capability model to gate what the vendor can actually run before it submits. Photo Lab Software sits on the other side of the same order: portrait products (prints, packages, wall art) rather than bound books.

photostudio.software is the studio management product for portrait, family, senior, and commercial photography studios: booking and scheduling on the shared slot engine, consent-railed client galleries with proofing, and the one agreements engine. The studio side and the lab side are connected — a studio’s orders flow to the lab port — but the operator surfaces are distinct. A studio operator who wants the booking and gallery tools starts at photostudio.software. A lab operator who wants to understand the order format and the routing layer starts here.

schoolphoto.studio is the school picture-day operator side: territories, the per-part handler matrix, district scheduling coordination, and the school-side data wall. Orders placed through the picture-day operator flow downstream to the lab port; the routing layer is transparent to the school-program operator surface. The operator does not configure lab routing; the platform routes the order.

What is in progress and what is planned

These capabilities are not presented as available today. They are named so a lab or operator considering this platform can see the honest state of each item, rather than discovering gaps after committing.

Per-vendor live submit (credential-gated)

The lab port, the adapter code, the manifest builder, and the idempotency spine are all built. The live submit path per vendor is gated on credentials and a test order being in place. When a vendor’s credentials are configured and a test order succeeds, that adapter’s live path is enabled. Until then, a live submit attempt on that adapter throws a transport-disabled error (e.g. RplTransportDisabledError for the RPL adapter) rather than placing an order. This is the honest-off posture: the code is built; the gate is a configuration condition, not a code gap. Early access

Lab-facing console and order dashboard

The platform-side order tracking and status dashboard for lab orders — the surface where a lab operator (or the studio operator acting on behalf of the lab) can see the current production state of submitted jobs, pull tracking numbers, and review the job history — is in development. The underlying data model (photo_fulfillment_job, photo_lab_route, photo_shipment) is built; the operator-facing console is the next step. In progress

Nightly cost-model sweep and ranker tuning

The routing-rules engine includes a cost-SLA ranker (SKU_QUALITY_FLOOR, rankCandidates) that selects among candidate adapters by landed cost and estimated ship days. The ranker is built; the nightly sweep that refreshes quotedAt freshness and updates the cost model from live adapter quotes is planned alongside the credential-gated live path for each vendor. Planned

Webhook endpoint hardening per vendor

Each adapter implements verifyWebhook() with the vendor’s own signature scheme. The webhook-apply logic (decideApply) maps normalized events to fulfillment-ladder advancements. The full durable-lease apply pipeline — the webhook route, the lease ledger, and the per-adapter endpoint hardening — is planned as each vendor’s live path is enabled. Planned

Common questions

What does “capabilities before quote” mean in practice?

Before the router asks an adapter for a price or submits an order, it calls capabilities() on that adapter. capabilities() returns the adapter’s owned CapabilitySet synchronously, with no network call. The CapabilitySet declares the routing modes the adapter serves, the SKU classes it can print, the product codes in its catalog, and the print sizes it accepts. If the job requires anything not in that set, the router denies it at the capability check — before quote(), before submit(), before any network request. The denial is a plain-language reason, not a silent failure.

How does idempotent submit prevent double-orders?

The platform builds an entry_reference token for each fulfillment job and stores it in photo_fulfillment_job.entry_reference. Every call to submit() carries this token alongside the FulfillmentJob. Each adapter maps that token to the lab’s own idempotency mechanism: WHCC’s import token, the SFTP manifest file name for VolumeLab, or a vendor-specific idempotency header for REST adapters. If a submit is retried — because of a network timeout, a process restart, or a backoff-and-retry cycle — the lab sees the same entry_reference and returns the existing order reference rather than creating a new one. The decideSubmit function in idempotency.ts checks the current route row state before any submit attempt to ensure the idempotency spine is maintained end-to-end.

What is a pony sort and why does the port model it as a first-class concern?

A pony sort is the physical sort a volume photo lab performs on a batch of portrait packages: each student’s package is labeled with their teacher and homeroom so the school can distribute the whole crate by walking classroom to classroom, not by sorting packages individually. The PSPA Index.txt manifest tells the lab how to sort. The port models this because the routing decision — whether a job should be dropshipped to a home or sorted into a school crate — is a capability of the adapter, not a post-routing configuration option. An adapter that cannot perform a pony sort cannot receive a Rail B job. The pony_sort flag in CapabilitySet makes that constraint structural and enforced at the routing layer.

Why are live submits early access instead of live today?

Live submits require vendor credentials (API keys, SFTP accounts, or vendor-managed accounts) and a confirmed test order that validates the integration end-to-end. The port and adapter code are built for every adapter. The live path per vendor is gated on those prerequisites being in place. This is the honest-off posture: the code exists, the transport seam is injected, and a transport-disabled error is thrown on any live submit attempt until the prerequisites are met. We say this plainly rather than describing the whole port as a live money-moving service.

Is this related to printlab.software?

They are related but distinct. printlab.software is the press/print-production side: the per-vendor capability profiles for press plants (binding styles, paper stocks, lamination, tip-ins, cover treatments, publication types), the producibility gating engine, and the print-ready preflight for yearbooks and publications. Photo Lab Software is the photographic lab side: portrait print orders, PSPA package manifests, and vendor routing for labs that produce photo prints — not bound books. The two packages (@homeroom/lab-routing and @homeroom/print-delivery) are distinct codebases with distinct port shapes. Neither calls the other.

What consent and eligibility checks happen before a job reaches the adapter?

Every FulfillmentJob carries two gate flags the router checks before routing: consentCleared and commerceEligible. consentCleared means the commerce-purpose consent decision was resolved and permitted for this specific likeness before the job was created. commerceEligible means the photo passed the platform’s QC and commerce-eligibility wall. The router fail-closes on either flag being false: a job with consentCleared=false or commerceEligible=false is never routed to an adapter. No likeness ships without a consent grant. The consent-gate module (packages/lab-routing/src/consent-gate.ts) provides the typed ConsentGateError and the assertJobConsentCleared check the router uses.

Does this replace the existing lab relationships studios or schools have?

No. The port is an integration layer, not a replacement for a lab relationship. A studio that already works with WHCC, a volume lab, or another provider continues that relationship. The port normalizes how the platform submits orders to those labs and tracks their status, so the studio’s order management stays in one place rather than split across per-lab integrations. The platform does not negotiate pricing with labs on behalf of a studio; the cost model the ranker uses is the landed cost the adapter quotes from the lab’s own API.

How does the RPL XML adapter differ from the REST adapters?

The RPL adapter builds a conformant RPL v1.0 structured XML order rather than a REST JSON payload. The RPL v1.0 spec uses an OfferingID + Quantity item model; ProductID and Price fields are deprecated per the spec. The XML build pipeline — buildRplOrder, renderRplXml, emitRplOrderXml — is complete and produces conformant XML from a FulfillmentJob. The transport (RplDisabledTransport) throws RplTransportDisabledError on any live submit until credentials are configured. The adapter implements the same Lab interface as every other adapter, so the router can test-route to it without a live network dependency.

Related products

printlab.software

The press/print-production side: per-vendor capability profiles, producibility gating, and print-ready preflight for yearbooks and publications. Distinct from the photographic/package lab workflow here.

yearbooklab.software

The yearbook-production toolchain that also outputs to capable press vendors. Distinct from photographic-lab portrait fulfillment; uses the print-delivery capability model.

pholio.photos

The family-facing school-photography product the lab pipeline ultimately serves: the consent-gated parent gallery, portrait proofing, and the family’s entry point to the school picture-day record.

schoolphoto.studio

The school picture-day operator side: territories, the per-part handler matrix, district scheduling, and the studio that places the portrait orders that flow downstream to the lab port.

homeroom.software

The flagship platform brand home: the full product story, the persuasion case, and the commercial details for the platform that the lab-routing layer is part of.

What is built and what is honest-off

The lab-agnostic ordering port — one Lab interface, capabilities-before-quote, idempotent submit keyed on our entry_reference — is built and running today in the platform. Ten vendor adapters — bay, cloudprinter, finerworks, gelato, generic-dropship, prodigi, rpi, rpl-xml, volume, whcc — spanning DropshipLab REST, VolumeLab PSPA/SFTP, and the RPL XML dialect, all normalized to the same result vocabulary, are built today. The PSPA pony-sort package world — the pony_sort flag in CapabilitySet, PonySortRow, the PSPA manifest builder, the Rail B routing constraint — is built today. The normalized production vocabulary (ProductionStatus, LabWebhookEvent, Shipment, Quote) across every adapter is built today. Live submits per vendor are early access, gated on credentials and a test order being in place per vendor — the code is built, the transport is honest-off, and no live money movement is claimed for any adapter today. We say all of this plainly. No partner or licensing offer appears on this page; that lane is founder-parked. No competitor brand names appear on this page. No stock phrases. The product is the port, the adapters, and the early-access lab-facing tools.

Photo Lab Software is a Stanley Studios product — the lab-integration layer of the platform that also includes printlab.software (press/print-production) and yearbooklab.software (yearbook-production toolchain). The photographic/volume-lab side: order intake, PSPA package world, vendor routing. Not press. Not yearbook composition. The lab side.