State-mandated compliance for nurse practitioners (NPs) and physician assistants (PAs), known together as advanced practice providers (APPs), is complex and one of the most underestimated constraints on healthcare business growth. Zivian Health’s platform operationalizes these rules for healthcare organizations, removing the manual administrative work required to deploy and manage APP workforces at scale.
Much of the complexity of APP compliance comes from the formal clinical oversight relationships many states require as a condition for APPs to practice. These clinical collaborations pair an APP with a collaborating physician who provides required oversight, and the oversight a physician must provide varies widely by state. In addition, some states require compliance filings for a collaboration to be valid, and others require a formal medical board approval process.
For the teams managing these relationships, that regulatory variation becomes a major operational problem. Every collaboration has to move through the right sequence of agreements, signatures, filings, approvals, and internal decisions based on the rules that apply to that provider and state.
Take onboarding a single collaboration between a PA and a physician. It begins in our CRM as an opportunity, becomes a collaboration in our platform, goes to an e-signature service for the collaborative agreement, then moves into board filing, where the path depends on scope of practice, prescribing authority, and the requirements of the state medical board. Once these gates have been cleared, the collaboration can go live, and Zivian providers can see their status, share charts, chat, and see billing and payment in the platform.
Each of those steps touches multiple systems, and at any point the process may need to stop and wait for an operator. That happens when a rule can’t be codified, or when the right call needs a manual lookup. The system has to pause the in-flight process, notify a Zivian subject matter expert, and wait for their ruling before it carries on.
Even more, our enterprise customers administer dozens or hundreds of collaborations, and both Zivian’s internal team and its customers need to know where each one stands in real time.
So every process we build shares four needs:
The onboarding process existed before we built it in Conductor. It was a series of manual steps and point-to-point integrations. The CRM would trigger a hook in the e-signature system, the CRM would trigger a hook in our platform APIs, and the e-signature system would trigger a hook in our platform APIs. In some cases the CRM called our APIs, which called the e-signature system, which called our APIs again. Every system knew about every other one. That’s a great way to get off the ground fast, which is critical for a small company, but eventually the seams start to show and the process breaks down more often than not. Many of those triggers also started with an operator clicking a button, so human error was built in.
We could have kept building on that, with our own models, async runners, retry logic, and failure handling. That’s negative engineering, a term Prefect popularized for code that handles failure but adds no value to the product. The value of the Zivian platform is the configurable rules, the third-party integrations, and knowing when to ask an expert. It isn’t in building retry logic or debugging async runners. We wanted to orchestrate our processes on top of existing fault tolerance, durability, and observability patterns.
Our needs were straightforward: less negative engineering, more observability and fault tolerance, and a clean fit with the tools we know, namely Python. We also wanted our operations and product support teams to have visibility and control, which meant a UI where they could follow and investigate running workflows.
I’ve worked with Airflow, Prefect, Camunda, and Conductor, and spent a short time with Temporal. We use Airflow at Zivian for data pipelines, but Airflow and Prefect are built for pipeline orchestration, not microservice orchestration. Camunda, Temporal, and Conductor are all strong in this space, so the decision came down to fit. When we started in February 2025, Camunda’s Python client was community-maintained, with no officially supported SDK (Camunda released one in April 2026). Temporal’s timeline view is excellent, but Temporal workflows are code, so the UI shows what a workflow has done rather than a diagram of the whole workflow. Our operations team needed the diagram.
After some local prototyping, we chose Conductor. The UI gave operations and support clear visibility into running workflows, the Python SDK got us moving quickly, and the project’s longevity gave us confidence it was well used and well supported. Partnering with Orkes let us try it quickly as a proof of concept, and gave us a crucial feature not readily available in open source Conductor: Human Tasks with a pre-built UI for operators. Human Tasks are simple — a predefined form (using JSON Forms) and a way to complete it in the Orkes UI — and very powerful. They let us automate a process even when we didn’t have every answer needed to fully automate it. In health care compliance, there are always edge cases. When one comes up, a Human Task asks a subject matter expert, while the fully automated cases follow the same configurable, observable flow.
Our first project with Conductor automated a process tracked in spreadsheets, with a lot of manual overhead. Multiple operators relied on clicking buttons across our CRM, our billing system, and our application at the right time. The workflow validated the operator’s input against the state of all three systems, emailed customers about the account change, used a Human Task to get more details from operators when needed, then waited until the event date and made the system changes.
The improvement was immediate. There were fewer mistakes, the execution was visible in the Orkes UI, and operators were freed for more valuable work. When an error occurred, it was clear in the logs and the UI, and when the process needed support or a configuration change, engineering and product support both had more control. That same workflow still runs the same process, over a year and a half later.
Following that success, we took on onboarding. It was a huge undertaking: bring a new collaboration in from the CRM, generate and sign the collaborative agreement with our e-signature vendor, walk through the state medical board filing step by step (the work is manual, but it needs step-wise processing and status updates), and finally, once every gate had passed, activate the collaboration so the APP could begin patient care.
With Conductor, we flipped the problem on its head. Instead of point-to-point integrations, every automated call and human touch point became part of a workflow. One “umbrella” workflow coordinates a sequence of smaller, purpose-driven workflows, each representing a stage of onboarding. Where the same steps recur, sub-workflows (workflows inside workflows) encapsulate them.
Our highest objective was to write status into our application database at every step, so we could surface it in our application UI. That’s what gives enterprise customers their real-time status, and it means our users never need to see Conductor at all.
Tasks were designed to be small and idempotent. Some read or write external systems, some update only our own system, Human Tasks sit wherever a decision is needed, and others send notes to operator channels when someone needs to act.
This project came with a much larger learning curve. We had to learn how Conductor really works (versioning, task scheduling, retry configuration and semantics, task I/O) and work out how to make our own tasks idempotent, which isn’t always straightforward with external systems.
Versioning turned out to be a tool rather than a trap. When we changed a workflow in a way that wasn’t backwards compatible, we bumped its version and had the caller pick the version that fit the inputs it had.
This is where the framework really began to pay off. We no longer wrote complicated retry logic or relied on webhook retries for redundancy; we used task retry configurations. We no longer wrote fully specced API endpoints just to update a database field; we used Python native tasks that call our service code and execute business rules directly. We no longer left retries up to chance; we built idempotency into our tasks so the system is fault tolerant from the start.
Not everything went smoothly, and each problem left a practice that every new workflow now follows:
Since going live, the onboarding workflow has carried several thousand collaborations through onboarding. We’ve made many improvements, with more in progress, but the building blocks are the same as the day it went live. Operations works the Human Tasks, and product support uses the Orkes UI to triage and fix issues. In total the engineering team has built 32 workflows, including license verification and more state medical board filing processes, and at last count we’ve executed 51,060 workflow instances in production.
If I were starting again, I’d use correlation IDs from day one. Finding a workflow is hard if you don’t have a record of it in your own database. Set each execution’s correlation ID to a unique, domain-specific string, such as the ID of the record it operates on, and finding workflows becomes much simpler.
Looking back at those four needs, the only part we build now is the part that’s ours. Our rules stay in our own code, called from native tasks. Durability comes from retry configuration and idempotent tasks. Human Tasks bring in an expert when a rule runs out. And every step writes status our users can see.
Join thousands of developers building the future with Orkes.