From guidewire-pack
Automate the PolicyCenter account→submission→quote→bind→issue→endorse→renew pipeline including the failure paths — underwriting issues blocking bind, quotes expiring before bind, referrals stuck pending approval, and mid-term endorsements that trigger unexpected premium audit recalculation. Use when building outbound integrations against PolicyCenter Cloud API (CRM-driven submission, broker-portal binding, automated renewal jobs). Trigger with "policycenter automation", "submission to bind", "policy renewal", "policy endorsement", "underwriting issue".
npx claudepluginhub flight505/skill-forge --plugin guidewire-packThis skill is limited to using the following tools:
Drive the PolicyCenter policy lifecycle through Cloud API and survive the state-transition failures that derail naive automation. This is the workflow used by broker portals to quote-and-bind, by CRMs to push submissions, and by renewal jobs to issue out-of-cycle. Assumes `guidewire-install-auth` provides the bearer token and `guidewire-sdk-patterns` provides the retrying client with checksum r...
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
Drive the PolicyCenter policy lifecycle through Cloud API and survive the state-transition failures that derail naive automation. This is the workflow used by broker portals to quote-and-bind, by CRMs to push submissions, and by renewal jobs to issue out-of-cycle. Assumes guidewire-install-auth provides the bearer token and guidewire-sdk-patterns provides the retrying client with checksum round-trip.
Five production failures this skill prevents:
200, the underwritingIssues[] array is non-empty, the client ignores it, bind returns 422 rule-violation.422 quote-expired on bind.Quoted status forever, blocking the next attempt.422 renewal-window-closed.guidewire-install-auth and guidewire-sdk-patterns (getToken(), patchResource(), paginate(), mapError())pc.account.write, pc.submission.write, pc.policy.write assigned to the integration's Service ApplicationPersonalAuto, BOPLine) — submission shape is product-specificBuild the workflow as discrete state-transition functions, each fully responsible for surfacing the failure modes of its transition. Compose them with explicit checkpoints — never collapse the pipeline into a single fire-and-forget call.
const idempotencyKey = crypto.randomUUID();
const account = await retryable(async () => {
const res = await fetch(`${BASE}/pc/rest/v1/accounts`, {
method: "POST",
headers: { Authorization: `Bearer ${await getToken()}`, "Content-Type": "application/json", "Idempotency-Key": idempotencyKey },
body: JSON.stringify({ data: { attributes: { accountHolderContact: contact, primaryLocation: address } } }),
});
if (!res.ok) throw await mapError(res, "POST", "/pc/rest/v1/accounts");
return (await res.json()).data;
});
The Idempotency-Key prevents duplicate accounts on retry. Persist account.attributes.accountNumber and the resource id immediately — both are needed downstream and the resource id is not derivable from the number.
Submission is product-specific; its attributes shape varies. Read the product's submission schema from the API reference rather than hardcoding fields. The submission moves to Draft on creation.
const submission = await createSubmission(account.attributes.id, {
productCode: "PersonalAuto",
effectiveDate: "2026-06-01",
});
The most-skipped step in naive automation. The quote response embeds underwritingIssues[]; non-empty with blocksBind: true means bind will fail.
const quoted = await fetch(`${BASE}/pc/rest/v1/jobs/${submission.attributes.id}/quote`, {
method: "POST",
headers: { Authorization: `Bearer ${await getToken()}`, "Idempotency-Key": idempotencyKey },
});
const body = (await quoted.json()).data;
const blockingIssues = body.attributes.underwritingIssues?.filter((u: any) => u.blocksBind) ?? [];
if (blockingIssues.length) {
await routeToReferralQueue(submission, blockingIssues);
return { status: "referred", issues: blockingIssues };
}
Some UW issues are informational and bind succeeds anyway; check the boolean per issue, not the array length. Surface blocking issues to the manual review queue with the originating actor's identity so the underwriter has context.
Quotes carry attributes.quoteExpirationDate. Past it, bind returns 422 quote-expired. Re-quote rather than retry.
if (new Date(body.attributes.quoteExpirationDate) < new Date()) {
return retryQuoteAndBind(submission.attributes.id);
}
const bound = await bindSubmission(submission.attributes.id);
After bind, the submission status moves to Bound and a Policy resource is created. bound.attributes.policy.id is the canonical policy reference for issuance, endorsement, and renewal.
Bind alone does not put the policy in force; issuance is a separate transition. A bound-but-not-issued policy is invisible to billing; downstream invoices will not generate.
const issued = await fetch(`${BASE}/pc/rest/v1/policies/${bound.attributes.policy.id}/issue`, {
method: "POST",
headers: { Authorization: `Bearer ${await getToken()}`, "Idempotency-Key": idempotencyKey },
});
const endorsement = await openEndorsement(policyId, effectiveDate);
const requoted = await quoteEndorsement(endorsement.attributes.id);
const oldPremium = currentPolicy.attributes.totalPremium;
const newPremium = requoted.attributes.totalPremium;
if (Math.abs(newPremium - oldPremium) > MATERIAL_DRIFT_THRESHOLD) {
await emitPremiumDriftEvent({ policyId, oldPremium, newPremium, delta: newPremium - oldPremium });
}
const boundEndorsement = await bindEndorsement(endorsement.attributes.id);
Threshold is policy-dependent; finance teams typically want any drift over a few hundred dollars surfaced before bind. Do not silently bind material drifts — they show up as billing surprises.
The renewal window is configured per product line. Hardcoding 60 days is wrong for any product with non-default settings; read the configuration or accept rejection from the API and respond to the renewal-window-closed error type.
const policy = await fetch(`${BASE}/pc/rest/v1/policies/${policyId}`).then(r => r.json());
const expirationDate = new Date(policy.data.attributes.expirationDate);
const renewalWindowOpens = subDays(expirationDate, 60);
if (new Date() < renewalWindowOpens) throw new Error("renewal-window-not-yet-open");
const renewalJob = await fetch(`${BASE}/pc/rest/v1/policies/${policyId}/renew`, { method: "POST" });
A complete PolicyCenter workflow integration ships with all of the following:
createAccount, createSubmission, quoteSubmission, bindSubmission, issuePolicy, openEndorsement, bindEndorsement, renewPolicy — each handling its own failures.const account = await createAccount(contact, address);
const submission = await createSubmission(account.attributes.id, { productCode: "PersonalAuto", effectiveDate });
const quoted = await quoteSubmission(submission.attributes.id);
if (quoted.blockingIssues.length) return { status: "referred", issues: quoted.blockingIssues };
const bound = await bindSubmission(submission.attributes.id);
const issued = await issuePolicy(bound.attributes.policy.id);
return { status: "issued", policyNumber: issued.attributes.policyNumber };
// Resume after underwriter approves the referral asynchronously
const submission = await getSubmission(submissionId);
if (submission.attributes.underwritingIssues.every(u => u.status === "Approved")) {
const requoted = await quoteSubmission(submissionId); // re-quote required after referral resolution
return await bindAndIssue(requoted);
}
const policy = await getPolicy(policyId);
const window = await getRenewalWindow(policy.attributes.productCode);
if (!isWithinWindow(policy.attributes.expirationDate, window)) {
return { status: "deferred", reason: "renewal-window-not-yet-open", retryAfter: window.opensOn };
}
const renewal = await renewPolicy(policyId);
return { status: "renewed", newPolicyId: renewal.attributes.id };
| Error | Cause | Solution |
|---|---|---|
422 with errors[].type === "rule-violation" on bind | unaddressed blocking UW issue | inspect quoted.attributes.underwritingIssues; never retry; route to manual review |
422 quote-expired on bind | quote past quoteExpirationDate | re-quote the submission; do not retry the bind directly |
422 renewal-window-closed | renewal initiated outside the configured window | back off until the window opens; surface retryAfter to the caller |
422 submission-state-invalid | trying to bind a submission still in Draft | call quote first; the state machine is enforced server-side |
409 Conflict on PATCH of submission | another job edited the submission concurrently | use patchResource() from guidewire-sdk-patterns to handle checksum round-trip |
| Bound policy never reaches In Force | bind was called but issue was not | issue is a separate API call after bind — both are required |
| Endorsement premium differs materially from expected | rate plan changed between policy inception and endorsement effective date | surface the drift to finance before binding; do not silently absorb the delta |
| Submission stuck in Quoted after a failed bind | partial-failure orphan | resume by checking underwritingIssues and re-attempting bind, or withdraw via the withdraw endpoint |
For deeper coverage (custom product schemas, line-of-business-specific submission shapes, multi-policy bind, broker hierarchy authorization), see implementation guide and API reference.
guidewire-install-auth — bearer token and scope assignment for the workflowguidewire-sdk-patterns — retrying client, checksum round-trip, idempotency, error mappingguidewire-core-workflow-b — the equivalent FNOL→reserve→settle pipeline in ClaimCenterguidewire-webhooks-integrations — listening to App Events that fire on bind, issue, and renewal transitionsguidewire-observability-and-incident-response — interpreting referral-queue depth and bind-failure signals in production