An eSignature API integration is not ready for production because one sandbox document got signed. It is ready when it behaves correctly while things go wrong: a response is lost after the provider accepted the request, the same webhook arrives three times, a worker crashes between receiving an event and saving it, or a contract is edited after a signing request was queued.
This guide is written for the backend engineering lead who owns the release decision. It proposes a test design, the Failure-Injection Matrix, that turns those failure modes into repeatable tests with explicit pass conditions. It is a proposed design for your team to adapt. It is not a certified method, and it does not replace legal review of your signing workflow.
- Scope: outbound requests that change state, inbound webhook events, state reconciliation, privacy of test data, and retrieval of completed evidence.
- Boundary: the code is illustrative. It is not the eSignly API contract. Confirm every endpoint, header and event field against the current API reference before you depend on it.
- Outcome: a set of all-required go/no-go controls, not a score that certifies readiness.
Key Takeaways
- Bottom line: test what your integration does when the outcome is unknown, duplicated, late or out of order. Happy-path tests cannot show this.
- An unknown result is not a failure and not a success. It means completion could not be confirmed. Reconcile against authoritative provider state before any retry of a consequential action.
- Bind each signing operation to a durable, tenant-scoped identity and an immutable approved payload hash. Reject a reused key that carries a different payload. Claim work atomically so concurrent workers cannot both send.
- Acknowledge a webhook only after the dedupe record and the follow-up work are committed together. Receiving a request is not the same as durably handling it.
- Identifiers are data too. Signer emails, document IDs and event keys can be sensitive, so test fixtures, logs and dedupe keys need an owner and a retention rule.
- Use go/no-go controls. Every control must pass. Passing them does not certify legal compliance or production readiness beyond what you tested.
Why eSignature Integration Defects Escape Staging
Key takeaway: signing workflows are long-lived and multi-party. Their worst defects appear in the gaps between systems, which sandbox demos do not exercise.
A signing request is not a single API call. It starts a process that can run for days across a signer's inbox, a mobile browser, your database and a provider's event system. Each hand-off is a place where a message can be lost, repeated or reordered. A staging run on a fast network with one cooperative signer exercises almost none of them.
The consequences are also unusual. A duplicated invoice is annoying. A duplicated or superseded signing request can leave a counterparty holding two versions of a contract. A lost completion event can mean a customer is never provisioned, or a regulated record never filed. That asymmetry is why testing deserves more design effort here than in a typical CRUD integration.
| Area | What staging usually shows | What production can do | Consequence if untested |
|---|---|---|---|
| Create request | Fast 2xx response | Provider accepts the request, then your client times out | Blind retry sends a second request to the signer |
| Webhook delivery | One event per action, in order | Duplicates, delays, replays, out-of-order arrival | Double fulfillment or a status that moves backward |
| Signer behavior | Signs immediately | Declines, lets it expire, opens a link after you voided it | Your state machine meets events it never modeled |
| Document content | One fixed template | Template edited while requests are in flight | Signer signs a version nobody approved |
| Test data | Fake names | Real emails copied into fixtures and logs | Personal data held without an owner or retention rule |
Many providers deliver webhooks at least once, which means duplicates are expected behavior and not a defect. Check your provider's documented delivery semantics and design for them. The HTTP semantics specification (RFC 9110) defines which methods are idempotent. A POST that creates a signing request is not one of them unless the service adds its own protection.
How Most Teams Test eSignature Integrations, and Why That Misses the Failures That Matter
Key takeaway: mocks and manual runs encode your assumptions. Fault injection tests the assumptions themselves.
Most teams use some mix of four approaches. Each is reasonable, and each leaves a specific gap.
- One manual end-to-end signature in a sandbox. It proves wiring and field placement. It says nothing about retries, duplicates or crashes.
- Mocked HTTP clients that return 200. The mock returns what the author expects. It never returns the ambiguous case where the provider acted but your client saw nothing.
- Load tests with synthetic traffic. They find throughput limits. They rarely check that two workers cannot both claim the same operation.
- Trusting provider retries. A provider retry policy helps delivery. It cannot make your handler idempotent or make your acknowledgment durable.
A subtler problem is that these tests assert on status codes. The questions that matter are about state: how many signing requests exist for this operation, which payload version did the signer receive, and what does the ledger say after a crash?
| Approach | Catches | Misses | Relative effort | Use it for |
|---|---|---|---|---|
| Manual sandbox run | Field mapping, redirect flow | All concurrency and failure behavior | Low | Smoke check |
| Mocked client unit tests | Logic errors in your code | Wrong assumptions about the provider | Low | Fast feedback |
| Contract tests against documented schema | Field and type drift | Timing, ordering, duplicates | Medium | Upgrade safety |
| Failure injection (proposed here) | Ambiguous outcomes, duplicates, crashes, races | Provider-side defects you cannot trigger | Medium to high | Go/no-go evidence |
| Live canary with internal signers | Real environment differences | Rare edge cases at volume | Medium | Final confirmation |
None of these replaces the others. The gap in most plans is the fourth row, and that is where this article concentrates. Related reading on idempotency, retries and webhook resilience covers the design principles. This article covers how to prove they hold.
A Proposed Design: The Failure-Injection Matrix
Key takeaway: cross four integration directions with a short list of faults, and write one invariant per cell. If you cannot state the invariant, you have found a design gap.
The Failure-Injection Matrix is a proposed design, not an established standard. It organizes tests along two axes. The first is direction: outbound mutation (you ask the provider to do something), inbound event (the provider tells you something happened), state read (you check what is true), and evidence retrieval (you fetch the completed document and audit record). The second is the fault applied to that direction.
| ID | Direction | Fault injected | Invariant that must hold |
|---|---|---|---|
| T1 | Outbound | Provider accepts, response is dropped | Operation becomes UNKNOWN. Reconcile runs before any resend. At most one signing request exists. |
| T2 | Outbound | Same operation key, different payload | Rejected locally. Nothing is sent. |
| T3 | Outbound | Two workers pick up the same operation | Exactly one claim succeeds. One provider call. |
| T4 | Outbound | Legitimate later action on the same contract (amendment, correction) | Gets a new operation identity. It is not suppressed as a duplicate. |
| T5 | Inbound | Same event delivered three times | One business effect. Two ignored deliveries are logged. |
| T6 | Inbound | Events arrive out of order or late | State never moves backward. Terminal states are protected. |
| T7 | Inbound | Process killed after receipt, before commit | No 2xx was returned. The event is recoverable on redelivery or replay. |
| T8 | Inbound | Forged or unsigned request | Rejected and not persisted as trusted. Verification follows the provider's documented scheme. |
| T9 | State read | Reconcile call itself times out | Operation stays UNKNOWN and escalates. It does not default to success or failure. |
| T10 | Evidence | Completed document fetch fails after the completion event | Retried with backoff. Completion is not marked archived until the file and its hash are stored. |
The Two Records the Matrix Depends On
Every invariant above relies on two durable records that your own database owns. The first is an operation ledger: a tenant-scoped identity for each business intent, bound to a hash of the approved payload. The second is an inbox with an outbox: a place where an incoming event is deduplicated, recorded and turned into scheduled work in a single transaction.
-- Illustrative schema, not an eSignly API contract CREATE TABLE signing_operation ( tenant_id TEXT NOT NULL, operation_id TEXT NOT NULL, -- durable business identity, e.g. 'msa:8841:amendment-2:send' payload_sha256 CHAR(64) NOT NULL, -- hash of the approved, immutable payload state TEXT NOT NULL DEFAULT 'PENDING' CHECK (state IN ('PENDING','CLAIMED','CONFIRMED','FAILED_TERMINAL','UNKNOWN')), claimed_by TEXT, lease_expires_at TIMESTAMPTZ, provider_ref TEXT, -- provider reference, once known PRIMARY KEY (tenant_id, operation_id) ); -- Atomic claim: exactly one worker sees rowcount = 1. -- The payload hash is part of the predicate, so a changed payload under the same key claims nothing. UPDATE signing_operation SET state = 'CLAIMED', claimed_by = :worker_id, lease_expires_at = now() + interval '2 minutes' WHERE tenant_id = :tenant_id AND operation_id = :operation_id AND payload_sha256 = :payload_sha256 AND state = 'PENDING'; -- Sweeper: an expired lease means we cannot know what the worker did UPDATE signing_operation SET state = 'UNKNOWN' WHERE state = 'CLAIMED' AND lease_expires_at < now();Two details are deliberate. First, the claim is one conditional UPDATE in your own database, which is a real compare-and-set. A separate read, a comparison in application code and a later write would not be, because two workers can both pass the comparison. Second, an expired lease moves the operation to UNKNOWN, never back to PENDING. The crashed worker may have already reached the provider.
There is an honest limit. This protects your side. Whether the provider call itself can be made exactly-once depends on what the provider offers, such as a client reference, an idempotency header or a lookup by your identifier. If it offers none of these, you can narrow the window and reconcile by listing, but you cannot promise exactly-once creation. Say so in your design record.
Running the Matrix: Five Test Suites for the Release Owner
Key takeaway: each suite has one job, and each can run in a development environment with synthetic signers and a fault proxy between your service and the provider.
Suite A: Outbound Mutations (T1 to T4)
The most important test is T1. Place a fault proxy between your sender and the provider that forwards the request, then drops the response. Assert that your service marks the operation UNKNOWN, calls the provider's read path to see whether a request exists, and sends nothing further until that read returns an authoritative answer. The reconcile-first recovery design for timeouts describes the production behavior this test protects.
# Illustrative test: an ambiguous timeout must reconcile, never blindly retry def test_timeout_after_provider_accepted(harness): op = harness.create_operation(tenant='t-a', operation_id='msa:1:send', payload=APPROVED) harness.fault_proxy.next_call(forward_then_drop_response=True) send_worker.run_once() assert ledger.state(op) == 'UNKNOWN' assert harness.provider_requests_for(op) == 1 reconciler.run_once() # reads authoritative provider state first assert harness.provider_requests_for(op) == 1 # still one assert ledger.state(op) in ('CONFIRMED', 'FAILED_TERMINAL') # only after a definite answerT2 changes one clause in an approved contract, reuses the operation identity, and expects local rejection. This follows the same principle as the IETF Idempotency-Key header draft, which says a key must not be reused with a different request payload. T3 starts two workers against one pending operation and asserts a single provider call. T4 guards against the opposite error: a correction or amendment is a legitimate new operation, so it must receive a new identity and, where relevant, supersede the old request deliberately. The supersession design for void, correct and re-send covers that path.
Suite B: Inbound Events (T5 to T8)
You usually cannot make a provider send duplicates on demand, so test your receiver with a replay tool that posts recorded or synthetic events. Send each event three times, shuffle ordering, and kill the process mid-handler. The key design rule is that the dedupe record, the durable event record and the scheduled work commit together, and the 2xx comes after the commit.
-- Illustrative: dedupe, durable record and work scheduling in ONE transaction BEGIN; WITH ins AS ( INSERT INTO webhook_inbox (tenant_id, dedupe_key, body_sha256, raw_body_ref, received_at) VALUES (:tenant_id, :dedupe_key, :body_sha256, :raw_body_ref, now()) ON CONFLICT (tenant_id, dedupe_key) DO NOTHING RETURNING dedupe_key ) INSERT INTO work_outbox (tenant_id, task_type, inbox_key, run_after) SELECT :tenant_id, 'apply_signing_event', dedupe_key, now() FROM ins; COMMIT; -- Return 2xx only after COMMIT succeeds. If anything fails, do not return 2xx.Choose dedupe_key from the provider's event identifier if its documentation defines one. If the payload carries only fields such as an event name, a document reference and a timestamp, derive a key from stable fields and hash it, so you do not store a signer's email address in an index or a log line. The public eSignly API page shows a sample event with an event name, document ID, signer email and signed-at time, so confirm in the current reference which fields are guaranteed and whether a unique event ID exists. For T8, verify authenticity using the scheme the provider documents. The HMAC specification (RFC 2104) describes the underlying construct, but the header name, algorithm and signed content are provider-specific. The dead-letter, replay and privacy design extends this suite to terminal failures.
Suite C: Reconciliation and Unknowns (T9)
Test the reconciler as seriously as the sender. When the read call also fails, the operation must stay UNKNOWN, an alert must fire, and a person or a scheduled job must make the next decision. Add an age limit so an operation cannot remain unknown without an owner.
Suite D: Privacy of Test Data and Identifiers
The NIST guide to protecting personally identifiable information (SP 800-122) is a useful lens: an email address plus a document reference can identify a person and reveal a business relationship. Treat test environments accordingly.
- Use synthetic signers on domains and mailboxes you control. Do not assume a sandbox or development mode suppresses real email delivery unless the provider confirms it.
- Scan application logs and traces from a full test run for signer emails, API keys and signing URLs. Fail the build if any appear.
- Assign an owner for each stored identifier, a retention period and a deletion path. Whether your organization is a controller or processor under GDPR, or handles protected health information under HIPAA, is a question for your own counsel. A vendor's compliance statements do not answer it for you.
Suite E: Evidence Retrieval (T10)
A completion event is a signal, not the evidence. Test that your service fetches the completed document and the audit record, stores them with a hash, and marks the contract archived only after storage succeeds. If your retention horizon is long, add a periodic integrity check. The guide to legally defensible audit trails explains what that record should contain.
Why This Fails in the Real World: Four Failure Patterns
Key takeaway: capable teams ship these defects because responsibility for the in-between states belongs to nobody, not because they were careless.
The scenarios below are illustrative composites built from common integration patterns. They are not customer results, and no figures are implied.
Pattern 1: The Helpful Retry Layer
A team uses a standard HTTP client with automatic retries enabled for transient errors. A request to create a signing request times out after the provider has already accepted it. The library retries silently. The signer receives two requests. Nobody wrote a bug: the retry setting was a sensible global default. The system gap is that no one owned the rule that consequential creates must not retry without reconciliation, and no test ever dropped a response after acceptance.
Pattern 2: Acknowledged, Not Saved
A webhook handler returns 200 immediately, then writes to a queue in a background thread. During a deployment the process stops between those two steps. The provider saw a success, so it does not redeliver. The completion event is gone, and the downstream account is never provisioned. The team believed that acknowledging fast was the best practice, and it is, but only if the durable write is part of what the acknowledgment means. A kill-the-process test (T7) finds this in minutes.
Pattern 3: One Key for Two Intents
An operation key is derived only from a contract ID. When legal edits the contract and the system resends, the key is the same. Depending on the implementation, the second request is either suppressed as a duplicate, so the signer keeps the old version, or it is sent as new with no link to the first. Both outcomes come from the same gap: the key did not represent the intent, and the approved payload hash was not bound to it. T2 and T4 together catch both directions of this error.
Pattern 4: Fixtures Become Records
Engineers seed a test environment with copies of real customer emails to make demos realistic. Verbose request logging then writes those addresses, along with signing links, into a shared log platform with a long retention period. No individual decided to retain personal data. The process had no rule for test-data provenance, and nobody had been named the owner of identifiers in logs.
| Pattern | Why smart teams still miss it | Test that exposes it | Governance fix |
|---|---|---|---|
| Helpful retry layer | Defaults look safe in code review | T1, T9 | Retry policy per operation class, owned by the integration lead |
| Acknowledged, not saved | Fast ACK is taught as best practice | T7, T5 | Definition: ACK means committed |
| One key for two intents | Keys are chosen for convenience | T2, T4 | Key schema reviewed with the payload hash |
| Fixtures become records | Realistic data feels responsible | Suite D log scan | Synthetic-only rule for non-production |
Mandatory Go/No-Go Controls, Trade-Offs and Limits
Key takeaway: every control below must pass. Do not add the results into a score. A high total with one failed control is still a no-go.
A weighted score invites trading a failed duplicate-handling test against passing performance tests. These controls do not trade. Treat each as a gate with recorded evidence: a test run ID, a log excerpt, or a reviewer sign-off.
| Gate | Required evidence | No-go if |
|---|---|---|
| G1: Unknown handling | T1 and T9 pass repeatedly | Any code path resends after an ambiguous timeout without reconciling |
| G2: Operation identity | Ledger keyed by tenant and operation, with payload hash | Key reuse with a changed payload is accepted |
| G3: Atomic claim | T3 passes with concurrent workers | The claim relies on read, compare, then write |
| G4: Legitimate later operations | T4 passes | Amendments or corrections are suppressed as duplicates |
| G5: Durable acknowledgment | T5 and T7 pass, with dedupe, record and scheduling in one transaction | 2xx can be returned before commit |
| G6: Ordering safety | T6 passes | A late event can reopen a terminal state |
| G7: Authenticity | T8 passes per the provider's documented scheme | Unverified requests can change state |
| G8: Evidence capture | T10 passes with stored hash | Completion is marked archived before the file is stored |
| G9: Privacy ownership | Log scan clean, named data owner, retention rule | Real signer identifiers exist in non-production, or a secret appears in logs |
| G10: Tenant isolation | Cross-tenant key and event collision test passes | Same operation or event key in two tenants can interfere |
Passing all ten means your tested failure modes are handled. It does not certify that the workflow meets ESIGN, UETA, eIDAS, HIPAA or any other legal regime. Those depend on your documents, your signers, your jurisdictions and your counsel's advice.
Trade-Offs to Plan For
- Effort: a fault proxy and replay tool take real engineering time. The cost is usually smaller than one duplicated contract incident, but weigh it against your volume and the consequence of an error.
- Flaky tests: timing-based faults can be unstable. Make faults deterministic, for example drop the Nth response, rather than random.
- Provider limits: eSignly's public listing describes a Development mode intended for testing, with limited API usage and possible watermarking, and a Live mode for production use. Budget test volume accordingly and confirm current limits in the API reference.
- Fidelity: a fake provider can drift from the real one. Use a contract-faithful fake for fast runs and a smaller set against the real development environment.
- Provider-side exactly-once: if the provider offers no idempotency control or client reference, G1 and G2 reduce risk but cannot eliminate it. Record that limitation.
When This Plan Does Not Apply
If you send a few signing requests per week from an internal tool and a person reviews each one, a full matrix is more than you need. A short checklist covering T1, T5 and Suite D is proportionate. The plan matters most when automation sends requests without a human watching, when volume is high, or when a duplicate or lost event has regulatory or contractual consequences.
A Lower-Risk Path to Go-Live, and What to Verify With Your Provider
Key takeaway: build the ledger and inbox first, then test in a development environment, then run a narrow live canary. Verify provider capabilities in the primary documentation, not in assumptions.
- Design the records first. Define the operation identity schema, the payload hash rule and the inbox before writing the sender.
- Build the harness early. The fault proxy and replay tool are part of the integration, not an afterthought.
- Run Suites A to E in a development environment with synthetic signers you control.
- Run a live canary. Use a small number of internal signers and real production configuration. Watch the count of
UNKNOWNoperations and dead-lettered events. - Expand gradually, and keep the unknown-age alert permanently on.
Verify Before You Depend On It
The public pages for the eSignly API and integrations describe a REST API over HTTPS, API key authentication, a free sandbox and development environment, and webhooks for events such as viewed, signed and completed. They also state that events are signed. Other points, such as an idempotency header, a client reference field, event identifiers and exact signature verification steps, should be confirmed in the current API reference and with eSignly's developer support. Use the table to record what you confirmed.
| Capability to confirm | Why your tests depend on it | If not available |
|---|---|---|
| Idempotency or client reference on create | Narrows the window in T1 and T3 | Reconcile by listing and matching your own metadata, and accept residual risk |
| Unique event identifier in webhooks | Defines the dedupe key for T5 | Derive a hashed composite key |
| Documented event signing method | Needed for T8 | Restrict by other controls and document the gap |
| Status read endpoint per request | Needed for reconcile in T1 and T9 | Polling by list, with rate-limit awareness |
| Retrieval of completed document and audit record | Needed for T10 | Do not mark archived until retrieval is proven |
| Rate limits and development-mode limits | Sizes your test volume | Throttle test runs |
Regarding assurance: eSignly states that it holds ISO 27001, SOC 2 Type II, HIPAA, GDPR, 21 CFR Part 11 and PCI DSS accreditations. Those are vendor statements. For your own vendor review, request current reports or attestations and check their scope. The eSignly security page is the place to start. Remember that a provider's certification covers the provider's controls. Your integration, your logs and your retention practices remain your responsibility.
2026 Update, Stated So It Stays Useful
As of late 2026, the Idempotency-Key header is still an active IETF Internet-Draft in the HTTP API working group (version 08), not a published RFC. That means providers implement idempotency in different ways today, and your tests should assert behavior, not a specific header. The durable principle is stable regardless of how that draft evolves: a key identifies one intent, and reusing it with a different payload is an error. For secure API design more broadly, the OWASP API Security Project remains a useful checklist to pair with this plan.
Want a safe place to run these tests?
Start with a free account, generate API keys, and rehearse the failure matrix before a real signer is involved.
Test your eSignature integration in a sandbox before you ship.
Free TrialConclusion: Decide With Evidence, Not Optimism
An eSignature integration earns trust through how it behaves under faults. Use these actions to move from a demo to a defensible release decision.
- Write down your operation identity rule. Include the tenant, the business intent and the approved payload hash. Confirm that a legitimate amendment gets a new identity.
- Add T1, T5 and T7 to your continuous integration pipeline this sprint. They cover the most damaging and most common failures: ambiguous timeouts, duplicates and lost acknowledgments.
- Name an owner for unknown states and for identifiers. Set a maximum age for
UNKNOWNoperations, and a retention rule for any signer data in logs and fixtures. - Record provider capability gaps in a design note, such as no idempotency control or no event ID, along with the compensating control you chose.
- Hold the release until all ten gates pass, and have counsel review the legal aspects of your workflow separately.
About This Guide
This article was prepared and reviewed by the eSignly Expert Team. eSignly has provided online eSignature SaaS and API services since 2014 and serves more than 100,000 users. eSignly states that it holds ISO 27001, SOC 2 Type II, HIPAA, GDPR, 21 CFR Part 11 and PCI DSS accreditations; request current attestations and confirm their scope for your own review. The code and the Failure-Injection Matrix in this article are illustrative proposed designs, and this article is not legal advice.
Frequently Asked Questions
How do you test an eSignature API integration beyond a sandbox demo?
Inject faults between your service and the provider, and assert on state rather than status codes. Cover at minimum:
- a response dropped after the provider accepted the request
- duplicate and out-of-order webhooks
- a process killed before a webhook is committed
- concurrent workers claiming the same operation
- a changed payload under a reused operation key
Each test should state one invariant, such as at most one signing request per operation.
What should I do when an eSignature API call times out?
Treat the result as unknown, not as failed. Record the operation as UNKNOWN, then read the authoritative request state from the provider before taking any other action. Resend only after you have a definite answer that no request exists. An unknown result means completion could not be confirmed. It does not mean success, and it does not mean verified failure.
Is an idempotency key enough to prevent duplicate signing requests?
Only if the provider honors it for that endpoint and your key is designed correctly. The key should represent one business intent, scoped to a tenant, and be bound to a hash of the approved payload. Reusing it with different content should be rejected. Confirm in the provider's API reference that idempotency is supported, and keep your own ledger regardless.
How should a webhook handler deduplicate events safely?
Record the event and schedule the follow-up work in one transaction, using a unique constraint on tenant and dedupe key. Return a success response only after the commit. Prefer the provider's documented event ID. If none exists, derive a hashed key from stable fields instead of storing plaintext signer emails.
Can I use real signer emails in a test environment?
Avoid it. Use synthetic signers on domains and mailboxes you control, and do not assume that a sandbox suppresses real email delivery. Emails and document references can identify people and relationships, so assign an owner and a retention rule to them. Your legal obligations depend on your role and jurisdiction, so consult counsel.
Does passing these tests mean my eSignature workflow is legally compliant?
No. The tests show that your integration handles specific technical failure modes. Legal validity under laws such as ESIGN or UETA depends on signer intent, consent, record retention, document type and jurisdiction. Vendor certifications describe the vendor's controls, not your entire workflow.
Why use go/no-go controls instead of a readiness score?
A summed score lets strong results in one area hide a serious gap in another. A duplicate-sending defect is not offset by good latency. Making each control a required gate with recorded evidence keeps the decision honest. Even then, passing the gates is not a certification of production readiness beyond what you tested.
Ready to test before you trust?
Ambiguous timeouts, duplicate events and lost acknowledgments are cheaper to find in a sandbox than in front of a signer. Build the harness first.
Create a free eSignly account, get your API keys and start rehearsing your failure matrix.
Free TrialResource Esignature Api Guide
This article is most relevant for CTOs and developers who need to reduce document-signing risk. Use the related eSignly path to compare plans, API options, compliance fit, and implementation next steps.
Reviewed for electronic signature decision makers
This guide is reviewed for clarity, legal and operational relevance, service alignment, and practical conversion path before being connected to an eSignly plan or API workflow.
For regulated, high-volume, or customer-facing workflows, validate legal duties, plan assumptions, and integration requirements with your internal stakeholders before rollout.

