cpq-api-and-automation

Use when programmatically driving Salesforce CPQ operations from Apex or REST — creating quotes, adding products, calculating pricing, saving quotes, amending contracts, or renewing contracts through the SBQQ.ServiceRouter API. Trigger keywords: CPQ API, SBQQ.ServiceRouter, QuoteCalculator, QuoteReader, QuoteSaver, QuoteProductAdder, ProductLoader, ContractAmender, ContractRenewer, programmatic quote, calculate callback, CPQ REST API. NOT for standard REST API or Salesforce Connect. NOT for declarative CPQ configuration such as price rules, discount schedules, or product rules. NOT for CPQ plugin interfaces (QuoteCalculatorPlugin, OrderPlugin).

Best use case

cpq-api-and-automation is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when programmatically driving Salesforce CPQ operations from Apex or REST — creating quotes, adding products, calculating pricing, saving quotes, amending contracts, or renewing contracts through the SBQQ.ServiceRouter API. Trigger keywords: CPQ API, SBQQ.ServiceRouter, QuoteCalculator, QuoteReader, QuoteSaver, QuoteProductAdder, ProductLoader, ContractAmender, ContractRenewer, programmatic quote, calculate callback, CPQ REST API. NOT for standard REST API or Salesforce Connect. NOT for declarative CPQ configuration such as price rules, discount schedules, or product rules. NOT for CPQ plugin interfaces (QuoteCalculatorPlugin, OrderPlugin).

Teams using cpq-api-and-automation should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/cpq-api-and-automation/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/apex/cpq-api-and-automation/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/cpq-api-and-automation/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How cpq-api-and-automation Compares

Feature / Agentcpq-api-and-automationStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when programmatically driving Salesforce CPQ operations from Apex or REST — creating quotes, adding products, calculating pricing, saving quotes, amending contracts, or renewing contracts through the SBQQ.ServiceRouter API. Trigger keywords: CPQ API, SBQQ.ServiceRouter, QuoteCalculator, QuoteReader, QuoteSaver, QuoteProductAdder, ProductLoader, ContractAmender, ContractRenewer, programmatic quote, calculate callback, CPQ REST API. NOT for standard REST API or Salesforce Connect. NOT for declarative CPQ configuration such as price rules, discount schedules, or product rules. NOT for CPQ plugin interfaces (QuoteCalculatorPlugin, OrderPlugin).

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# CPQ API and Automation

This skill covers all programmatic CPQ operations driven through `SBQQ.ServiceRouter` — the single correct entry point for creating, pricing, amending, and renewing CPQ quotes without using the CPQ UI. Activate when Apex code, a batch job, an integration, or an external system needs to drive CPQ operations that must respect the pricing engine.

---

## Before Starting

Gather this context before working on anything in this domain:

- **Confirm CPQ managed package is installed.** All API classes live in the `SBQQ` namespace provided by the CPQ managed package. If `SBQQ.ServiceRouter` is not accessible, the package is not installed or the running user lacks the CPQ permission set.
- **Know the operation type.** Each CPQ operation maps to a specific loader string. Using the wrong loader string produces a runtime error or silently returns an empty model. Confirm whether the task is read, product-add, calculate, save, amend, or renew before writing any code.
- **Count the quote lines.** Synchronous quote calculation degrades noticeably past approximately 100 lines and can hit CPU governor limits in complex orgs. For high line-count quotes, plan to use the async calculate path or Large Quote Mode.
- **Never use direct DML on SBQQ objects for pricing fields.** Direct `insert` or `update` on `SBQQ__QuoteLine__c` bypasses the CPQ pricing engine. Fields such as `SBQQ__NetPrice__c`, `SBQQ__CustomerPrice__c`, `SBQQ__Discount__c`, and `SBQQ__RegularTotal__c` will be stale or inconsistent until the next full recalculation — which may overwrite your changes or produce corrupt totals on the quote.

---

## Core Concepts

### SBQQ.ServiceRouter — The Single Entry Point

All CPQ programmatic operations flow through `SBQQ.ServiceRouter`. The class exposes two key static methods:

- `SBQQ.ServiceRouter.read(String loaderName, String uid)` — reads a serialized JSON model for a given record.
- `SBQQ.ServiceRouter.save(String saverName, String model)` — persists a serialized JSON model back to the database.

A separate method handles calculation:

- `SBQQ.ServiceRouter.calculateInBackground(String quoteId, SBQQ.CalculateCallback callback)` — triggers async calculation and invokes the callback when complete.

The same operations are available via REST at `POST /services/apexrest/SBQQ/ServiceRouter` with a JSON body containing `loaderName` (or `saverName`) and `model` (a JSON-serialized string).

### Loader Strings

Each operation maps to a specific loader string that the `ServiceRouter` dispatches on:

| Loader String | Method | Purpose |
|---|---|---|
| `QuoteReader` | `read` | Read an existing quote and all its lines as a JSON model |
| `QuoteProductAdder` | `save` | Add one or more products to a quote model |
| `QuoteCalculator` | `save` / async | Re-price the quote model (sync or async) |
| `QuoteSaver` | `save` | Persist a calculated quote model to the database |
| `ProductLoader` | `read` | Load product catalog records into a quote-compatible product model |
| `ContractAmender` | `read` | Create an amendment quote model from an approved contract |
| `ContractRenewer` | `read` | Create a renewal quote model from an approved contract |

Passing an unrecognized string raises a runtime exception. Mixing read-phase loaders with save-phase savers (e.g., passing `QuoteReader` to `save()`) produces an error or a no-op.

### Async Calculate API and SBQQ.CalculateCallback

The `QuoteCalculator` operation used synchronously inside `ServiceRouter.save()` runs the pricing engine in the same transaction. For quotes with many lines, this consumes CPU time that accumulates against governor limits. Salesforce CPQ also exposes an **async calculate path**:

1. The calling code invokes `SBQQ.ServiceRouter.calculateInBackground(quoteId, callback)`.
2. CPQ triggers a queueable calculation job outside the current transaction.
3. When calculation completes, CPQ calls `callback.onCalculated(String quoteModel)` on the class that implements `SBQQ.CalculateCallback`.

The callback class must be `global` and implement `SBQQ.CalculateCallback`, which requires a single method: `void onCalculated(String quoteModel)`. Inside `onCalculated`, the implementation typically calls `SBQQ.ServiceRouter.save('QuoteSaver', quoteModel)` to persist the result.

Errors thrown inside `onCalculated` do not surface to the originating UI session. Robust implementations must log failures explicitly.

### Why Direct DML Corrupts CPQ Totals

The CPQ pricing engine maintains a consistent financial model across related fields on `SBQQ__QuoteLine__c` and `SBQQ__Quote__c`. These fields are not independent — they are outputs of a multi-pass calculation that evaluates price rules, discount schedules, block pricing, contracted prices, and subscription math in sequence.

Direct `update` on a quote line field (e.g., setting `SBQQ__Discount__c = 10`) skips all pricing passes. CPQ does not automatically recalculate downstream fields (`SBQQ__NetPrice__c`, `SBQQ__CustomerPrice__c`, `SBQQ__RegularTotal__c`, `SBQQ__Quote__c.SBQQ__NetTotal__c`, etc.). The line record and the quote header become financially inconsistent. On the next save through the CPQ UI, CPQ may overwrite your discount value with the engine's own calculated result.

Use `ServiceRouter` with `QuoteCalculator` + `QuoteSaver` to apply any programmatic field change that affects pricing.

---

## Common Patterns

### Pattern 1: Programmatic Quote Creation with Products and Calculation

**When to use:** An external system, batch process, or automation needs to create a complete CPQ quote with products and accurate pricing without a user opening the CPQ UI.

**How it works:**

1. Create a bare `SBQQ__Quote__c` record with `insert` (header fields only — no line manipulation).
2. Load the quote model: `String quoteModel = SBQQ.ServiceRouter.read('QuoteReader', quoteId);`
3. Load product records: `String productModel = SBQQ.ServiceRouter.read('ProductLoader', productId);`
4. Add products to the quote model: `String updatedModel = SBQQ.ServiceRouter.save('QuoteProductAdder', combineModels(quoteModel, productModel));`
5. Calculate pricing: `String calculatedModel = SBQQ.ServiceRouter.save('QuoteCalculator', updatedModel);`
6. Persist to database: `SBQQ.ServiceRouter.save('QuoteSaver', calculatedModel);`

**Why not direct insert of SBQQ__QuoteLine__c:** Inserting lines directly bypasses the product configuration model, skips bundle expansion, and produces lines with no pricing engine state. The resulting lines will have null or zero values for all calculated price fields.

### Pattern 2: Async Calculation for High Line-Count Quotes

**When to use:** The quote has more than approximately 100 lines, or the sync calculation path is hitting CPU governor limits or taking over 5 seconds.

**How it works:**

1. Implement a `global class MyCalculateCallback implements SBQQ.CalculateCallback`.
2. In `onCalculated(String quoteModel)`, call `SBQQ.ServiceRouter.save('QuoteSaver', quoteModel)` and log any errors.
3. Invoke: `SBQQ.ServiceRouter.calculateInBackground(quoteId, new MyCalculateCallback());`
4. The method returns immediately; calculation runs asynchronously as a queueable.
5. Poll or use a platform event to detect completion if the calling process needs to wait.

**Why not sync calculate:** Synchronous `QuoteCalculator` via `ServiceRouter.save()` processes all lines in the current transaction. Past ~100 lines, CPU consumption can breach the 10-second limit, causing an unhandled `LimitException` that rolls back the entire transaction.

### Pattern 3: Contract Amendment and Renewal via API

**When to use:** An integration or automation needs to programmatically create amendment or renewal quotes from approved contracts without a user clicking "Amend" or "Renew" in the UI.

**How it works:**

Amendment:
1. Read the amendment model: `String amendModel = SBQQ.ServiceRouter.read('ContractAmender', contractId);`
2. Modify the returned model JSON to change quantities, add products, or end-date lines.
3. Calculate: `String calculatedModel = SBQQ.ServiceRouter.save('QuoteCalculator', amendModel);`
4. Save: `SBQQ.ServiceRouter.save('QuoteSaver', calculatedModel);`

Renewal:
1. Read the renewal model: `String renewModel = SBQQ.ServiceRouter.read('ContractRenewer', contractId);`
2. Optionally modify the model (e.g., adjust quantities or pricing).
3. Calculate and save as above.

The contract must have `SBQQ__Status__c = 'Activated'` (approved). Passing an unapproved contract ID returns an error or an empty model.

---

## Decision Guidance

| Situation | Recommended Approach | Reason |
|---|---|---|
| Create a quote and add products programmatically | QuoteReader → QuoteProductAdder → QuoteCalculator → QuoteSaver | Follows the CPQ calculation pipeline in correct order |
| Re-price a quote after changing a field | QuoteReader → modify model → QuoteCalculator → QuoteSaver | Pricing engine must see the full model; partial updates are not supported |
| Quote has 100+ lines | Async calculate via calculateInBackground + CalculateCallback | Sync path risks CPU governor limit; async is the safe path |
| Amend an existing contract | ContractAmender → QuoteCalculator → QuoteSaver | ContractAmender sets up the amendment delta model correctly |
| Renew an expiring contract | ContractRenewer → QuoteCalculator → QuoteSaver | ContractRenewer clones subscriptions into a renewal quote |
| Add a product to a quote | ProductLoader + QuoteProductAdder | Load product into model format before adding to quote |
| Direct DML on SBQQ__QuoteLine__c for pricing fields | Never — use ServiceRouter | Direct DML bypasses pricing engine; produces corrupt totals |
| Call CPQ API from an external system | REST POST /services/apexrest/SBQQ/ServiceRouter | Same loaderName/model pattern over HTTP with OAuth session |

---

## Recommended Workflow

1. **Identify the operation and select the loader string.** Match the business requirement to the correct loader string from the taxonomy table. Confirm the input record (quote ID, contract ID, product ID) is available and the record is in the required state (e.g., contract must be activated for amendment/renewal).
2. **Read the current model.** Call `SBQQ.ServiceRouter.read(loaderName, recordId)` to obtain the JSON model. Do not construct the model JSON manually — the schema is internal to the CPQ package and changes across versions.
3. **Modify the model if required.** For add-product flows, load the product model separately and pass both to `QuoteProductAdder`. For field changes, parse the JSON, update the relevant fields, re-serialize, and pass to `QuoteCalculator`.
4. **Calculate pricing.** Pass the modified model through `QuoteCalculator`. For quotes under ~100 lines, use synchronous `ServiceRouter.save('QuoteCalculator', model)`. For larger quotes, use `calculateInBackground` with a `CalculateCallback` implementation.
5. **Save the calculated model.** Call `ServiceRouter.save('QuoteSaver', calculatedModel)` to persist all CPQ fields. Do not save the model by DML — always use `QuoteSaver`.
6. **Validate the results.** Query the saved `SBQQ__Quote__c` and spot-check key calculated fields (`SBQQ__NetTotal__c`, `SBQQ__GrossTotal__c`, line-level `SBQQ__NetPrice__c`). Confirm the totals are non-null and match the expected pricing.
7. **Handle errors explicitly.** `ServiceRouter` calls can throw runtime exceptions. Wrap each call in try/catch, log failures with context (loaderName, record ID, model snippet), and surface errors to calling processes. In async callbacks, log failures to a custom object or platform event since exceptions in `onCalculated` are silent.

---

## Review Checklist

- [ ] All CPQ operations go through `SBQQ.ServiceRouter` — no direct DML on SBQQ pricing fields
- [ ] Loader strings match the intended operation (read vs. save vs. async)
- [ ] Contract is in Activated status before calling ContractAmender or ContractRenewer
- [ ] Quote line count evaluated — async path used if count may exceed ~100 lines
- [ ] `SBQQ.CalculateCallback` implementation is `global` and handles errors explicitly
- [ ] REST calls include valid OAuth token and correct Content-Type: application/json
- [ ] Calculated model saved via `QuoteSaver`, not via DML
- [ ] Governor limits (CPU, heap, SOQL) reviewed in debug logs under realistic line counts
- [ ] Errors from ServiceRouter calls are caught and logged with sufficient context

---

## Salesforce-Specific Gotchas

1. **Direct DML on SBQQ__QuoteLine__c corrupts pricing totals** — Writing directly to price fields (`SBQQ__Discount__c`, `SBQQ__NetPrice__c`, etc.) bypasses all pricing engine passes. The quote header totals become stale and the line data is financially inconsistent. The next UI-driven save may silently overwrite your values. Always route field changes through the ServiceRouter model pipeline.

2. **Async calculate errors are silent** — Exceptions thrown inside `SBQQ.CalculateCallback.onCalculated()` do not propagate to the original calling context. If the callback crashes, the quote is left in a partially calculated state with no user-visible error. Implement explicit logging (custom object insert, platform event) inside `onCalculated` error handlers.

3. **Sync calculate degrades past ~100 lines** — The synchronous `QuoteCalculator` path runs in the current Apex transaction. On complex orgs with many price rules or configuration rules, CPU consumption per line can be high. A 150-line quote can easily consume 8–9 seconds of CPU, leaving little headroom for the rest of the transaction. Plan the async path for any quote that could grow beyond 100 lines.

4. **Model JSON schema is internal and version-specific** — The JSON returned by `QuoteReader`, `ContractAmender`, etc. reflects the internal CPQ data model for the installed package version. Manually constructing or hardcoding model JSON is fragile — field names and nesting change across CPQ releases. Always start from a `ServiceRouter.read()` response and modify in place.

5. **ContractAmender and ContractRenewer require Activated contracts** — Passing a contract with `SBQQ__Status__c` other than `'Activated'` returns an error or an empty model. This is not always obvious because the error message from ServiceRouter can be generic. Validate contract status before invoking these loaders.

---

## Output Artifacts

| Artifact | Description |
|---|---|
| Apex ServiceRouter invocation | Code calling `SBQQ.ServiceRouter.read()` and `save()` with correct loader strings |
| CalculateCallback class | `global class` implementing `SBQQ.CalculateCallback` for async calculation flows |
| REST call example | POST body shape for `/services/apexrest/SBQQ/ServiceRouter` |
| Loader string decision table | Mapping from operation to loaderName/saverName |
| Amendment/renewal workflow | Apex sequence for contract amendment or renewal via ContractAmender/ContractRenewer |

---

## Related Skills

- `apex/cpq-apex-plugins` — CPQ plugin interfaces (QuoteCalculatorPlugin, OrderPlugin) for hooking into calculation lifecycle; distinct from ServiceRouter-based API operations
- `admin/cpq-pricing-rules` — Declarative price rules that fire during quote calculation; understand these before using the API to override prices
- `admin/cpq-quote-templates` — Quote template configuration; required to understand how quote output relates to programmatic quote data

Related Skills

salesforce-cli-automation

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when automating Salesforce work with the unified Salesforce CLI (`sf`, v2): shell scripts, Make/npm tasks, cron jobs, and CI steps that need stable flags, `--json` output, org aliases, bulk/data commands, plugins, and non-interactive auth patterns. Trigger keywords: sf CLI automation, sfdx migration, JSON output CI, sf project deploy script, sf data bulk, CLI plugins, target-org alias, machine-readable CLI. NOT for choosing or wiring a specific CI platform (GitHub Actions, GitLab, Jenkins, Bitbucket, Azure DevOps—use those devops skills), VS Code Salesforce extensions, or Copado/Gearset release management UIs.

release-notes-automation

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when generating customer- or stakeholder-facing release notes from git history, Jira/ADO ticket links, and Salesforce metadata diffs at deploy time. Triggers: 'auto-generate release notes', 'changelog from commits', 'release notes from PR titles', 'what changed in this deployment'. NOT for managed-package version creation, push upgrades, or org assessment.

cumulusci-automation

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when configuring CumulusCI (cci) for Salesforce project automation: authoring cumulusci.yml tasks and flows, customizing or composing standard flows, integrating Robot Framework acceptance tests, and running cci flows in CI pipelines with JWT-based authentication. Covers task class_path/options wiring, flow step ordering, org and source declarations, cross-project reuse via sources, and the standard flow library (dev_org, qa_org, ci_feature, install_beta). NOT for raw SFDX/sf CLI workflows without CumulusCI, scratch org pool management (use scratch-org-pools), unlocked or managed package versioning (use unlocked-package-development or second-generation-managed-packages), or Salesforce-native DevOps Center pipelines (use devops-center-pipeline).

deployment-automation-architecture

8
from PranavNagrecha/AwesomeSalesforceSkills

Deployment automation architecture on Salesforce: pipeline orchestration, branch strategy, environment topology, quality gates, release trains. Selecting between Copado, Gearset, Flosum, and native SFDX + GitHub Actions. NOT for cloud-specific deploy mechanics (use cloud-specific-deployment-architecture). NOT for CI/CD tool tutorials.

cpq-test-automation

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when writing, reviewing, or debugging test classes for Salesforce CPQ (Steelbrick) functionality — including quote creation, price rules, contracting, ordering, and CPQ API integration. Trigger keywords: CPQ test class, SBQQ test, quote calculation test, price rule test, CPQ Apex test, ServiceRouter test, QuoteCalculator test. NOT for standard Apex testing patterns unrelated to CPQ, nor for UI/Selenium test authoring outside the CPQ context.

sandbox-post-refresh-automation

8
from PranavNagrecha/AwesomeSalesforceSkills

Automating the post-sandbox-refresh cleanup via the `SandboxPostCopy` interface — the Apex class that runs ONCE after a sandbox refresh / clone completes, before users can log in. Used to mask emails, deactivate users, scrub integration endpoints, disable scheduled jobs, repopulate sample data, and reapply any per-environment configuration that the metadata copy doesn't carry. Covers the interface contract (`runApexClass` setting, `SandboxContext` parameter, single-execution semantics), the email-masking pattern that prevents production emails firing from sandbox tests, and the runbook checklist of what every refresh should reset. NOT for the sandbox refresh-strategy decision (use devops/sandbox-strategy-designer), NOT for general bulk Apex (use apex/scheduled-apex).

process-automation-selection

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when deciding which Salesforce automation tool should own a requirement, including before-save and after-save Flow, screen Flow, scheduled Flow, invocable Apex, Apex triggers, and migration off Workflow Rules or Process Builder. Triggers: 'Flow or Apex trigger', 'which automation tool should I use', 'Process Builder migration', 'Workflow Rule retirement', 'same-record update or trigger'. NOT for detailed implementation of a Flow or trigger after the automation boundary has already been chosen.

marketing-automation-requirements

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when gathering, documenting, or validating requirements for a Salesforce marketing automation program — covering MCAE (Account Engagement / Pardot) lifecycle stages, MQL/SQL threshold definitions, scoring model specifications (sources, weights, decay, ceiling), handoff notification design, CRM field updates on status change, and sales SLA. Trigger keywords: MQL criteria, marketing-to-sales handoff, lead lifecycle, scoring requirements, automation program requirements, Marketing Cloud Automation Studio scope. NOT for implementation of MCAE automation rules, MCAE scoring configuration, MC Automation Studio SQL activity authoring, or Einstein Lead Scoring setup — those are covered by dedicated implementation skills.

xss-and-injection-prevention

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when writing or reviewing Visualforce pages, Apex controllers, or LWC components that output user-supplied data, build dynamic queries, or construct HTTP responses. Triggers: 'XSS in Visualforce', 'SOQL injection vulnerability', 'how to encode output in Apex', 'JSENCODE Visualforce', 'open redirect prevention'. NOT for Apex CRUD/FLS enforcement (use soql-security or apex-crud-and-fls), NOT for Shield encryption (use shield-encryption-key-management), NOT for AppExchange security review process (use secure-coding-review-checklist).

visualforce-security-and-modernization

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when hardening or modernizing legacy Visualforce pages — covers the platform CSRF token model and when disabling it is a security regression, view state encryption guarantees and the 170 KB ceiling, FLS/CRUD enforcement gaps on `<apex:outputField>` and on getters that return sObjects, `<apex:includeScript>` interaction with the org Content Security Policy, hosting LWC inside a VF page via `lightning:container` / `lightning-out`, and the retire-vs-harden-vs-leave-alone decision for an inventory of legacy pages. Triggers: 'should I rewrite this Visualforce page in LWC', 'CSRF protection disabled on Visualforce page is that safe', 'community user sees a field they should not on a Visualforce page', 'view state encryption is that enough for sensitive data', 'how do I host an LWC inside a Visualforce page', 'apex:dynamicComponent and apex:actionFunction safe to keep'. NOT for greenfield Visualforce architecture (use apex/visualforce-fundamentals — controller types, view state pattern selection, PDF rendering); NOT for Visualforce email template authoring (use apex/visualforce-email-templates if/when that skill is authored); NOT for general Apex security review across triggers and async (use apex/soql-security and security/secure-coding-review-checklist).

transaction-security-policies

8
from PranavNagrecha/AwesomeSalesforceSkills

Transaction Security policy creation and configuration: condition builder, enhanced policies, enforcement actions (block, MFA, notification, end session), real-time monitoring mode, and policy troubleshooting. NOT for Event Monitoring log analysis or Shield Event Monitoring setup (use event-monitoring). NOT for Apex testing or debug-log analysis.

sso-saml-troubleshooting

8
from PranavNagrecha/AwesomeSalesforceSkills

Diagnosing broken SAML SSO into Salesforce — IdP-initiated vs SP-initiated flows, signing-certificate validity / expiry, NameID format mismatches, RelayState handling, audience / entityId / issuer mismatches, clock skew, the SAML Assertion Validator in Setup, the Login History debug log, and the My Domain prerequisite for SSO. Covers the standard diagnostic loop: read the SAML response, identify which check failed, fix at the IdP or SP. NOT for OAuth / OpenID Connect SSO (see security/oauth-openid-troubleshooting), NOT for setting up SSO from scratch (see security/sso-saml-setup).