New Connector Implementation
Overview
This skill produces a complete payment connector in the UCS Rust codebase.
MANDATORY SUBAGENT DELEGATION: You are the orchestrator. You MUST delegate every step
to a subagent using the prompts in references/subagent-prompts.md. Do NOT implement
code, run tests, or review quality yourself. Spawn subagents and coordinate their outputs.
Output:
- Main connector file with macro-based flow implementations
- Transformers module with request/response types and conversions
- Registration in the connector registry
- All 6 core flows + any required pre-auth flows
- gRPC tested end-to-end
- Certification manifest (
connector_specs/{connector_name}/specs.json) that survives the
merge-blocking CI gate — see Step 6
Prerequisites:
- Tech spec at
grace/rulesbook/codegen/references/{connector_name}/technical_specification.md
- Rust toolchain with
cargo
Project Structure
| Purpose |
Path |
| Main connector file |
crates/integrations/connector-integration/src/connectors/{connector_name}.rs |
| Transformers module |
crates/integrations/connector-integration/src/connectors/{connector_name}/transformers.rs |
| Connector registry |
crates/integrations/connector-integration/src/connectors.rs |
| Enum definitions |
crates/common/common_enums/src/enums.rs |
| Domain utilities |
crates/types-traits/domain_types/src/utils.rs |
| Macro definitions |
crates/integrations/connector-integration/src/connectors/macros.rs |
| Superposition URLs |
config/superposition.toml |
URL patching (Connectors::apply) |
crates/types-traits/domain_types/src/types.rs |
Critical Conventions
These rules apply to ALL subagents. Include them in every subagent prompt.
- Use
RouterDataV2 (NEVER RouterData), ConnectorIntegrationV2 (NEVER ConnectorIntegration)
- Import from
domain_types (NEVER hyperswitch_domain_models)
- Connector struct MUST be generic:
ConnectorName<T>
- NEVER hardcode status values -- always map from connector response via
From/TryFrom
- Use macros (
create_all_prerequisites! + macro_connector_implementation!) for all flows
- Flows you do NOT implement are stubbed by
macro_connector_flow_status_impls!
(not_implemented: [...] / not_supported: [...]) -- see "The macro nobody remembers" below
- Check
references/utility-functions.md before implementing custom helpers
- No
unwrap(), no fields hardcoded to None, no unnecessary .clone()
- Auth data accessed via
req.connector_config (NOT connector_auth_type, which was
deleted from RouterDataV2). get_auth_header takes &ConnectorSpecificConfig
(crates/types-traits/interfaces/src/api.rs:25); copy the idiom from a recent connector
such as connectors/travelhub.rs
build_error_response takes THREE parameters --
(res: Response, _event_builder: Option<&mut events::Event>, _connector_config: &ConnectorSpecificConfig)
(interfaces/src/api.rs:50). The event type is events::Event; there is no ConnectorEvent
in this crate, and events::Event has no set_error_response_body method. The same
third parameter applies to get_error_response_v2 and get_5xx_error_response
ErrorResponse has 13 fields and DOES implement Default
(domain_types/src/router_data.rs), so prefer ..Default::default() over listing them.
attempt_status is Option<FlowStatus>, not Option<AttemptStatus> -- wrap it, e.g.
Some(FlowStatus::Payment(AttemptStatus::Failure)), and never force a terminal status on
the shared error path (exemplar: connectors/flywire.rs:362-370; minimal form
connectors/noon.rs:499-512)
PaymentsResponseData::TransactionResponse (11 fields) and RefundsResponseData
(4 fields) are read from domain_types/src/connector_types.rs. Enum struct-variants have
no functional-update syntax, so every omitted field is E0063 -- list them all
- Error codes/messages:
.unwrap_or_else(|| NO_ERROR_CODE.to_string()) and
NO_ERROR_MESSAGE, from crates/common/common_utils/src/consts.rs. Never
error_code.unwrap_or_default()
- Amount unit: read the vendor spec and match its wire format. The five types in
crates/common/common_utils/src/types.rs are MinorUnit, StringMinorUnit,
StringMajorUnit, FloatMajorUnit, StringTwoDecimalUnit. There is no safe default --
rg -o "amount_type: \w+" crates/integrations/connector-integration/src/connectors/*.rs
at HEAD splits ~10/10/9/4 across StringMajorUnit / MinorUnit / StringMinorUnit / FloatMajorUnit
- Status enums need
#[serde(other)] Unknown at the DESERIALIZATION layer; the
status-mapping match must stay exhaustive over named variants (no _ => there).
Reviewers require both halves
- An in-band failure returned with HTTP 2xx must produce
Err(ErrorResponse { .. }),
branching on a success predicate -- see utils::is_payment_failure in
crates/types-traits/domain_types/src/utils.rs
- Connector base URLs MUST be registered in
config/superposition.toml (dimension enum + sandbox &
production connector_base_url overrides) AND the connector wired into
Connectors::patch_connector_urls in crates/types-traits/domain_types/src/types.rs for dynamic
URL patching. The scaffold script (add_connector.sh) now does this automatically — verify it
landed, and pass --production-url when the live URL differs from the sandbox base URL.
Workflow: Orchestrator Sequence
Each step below is an independent subagent. The orchestrator delegates each step,
waits for completion, and passes outputs to the next step.
Full subagent prompts: references/subagent-prompts.md
Step 1: Tech Spec Validation (Subagent)
Subagent prompt: references/subagent-prompts.md → Subagent 1
Inputs: connector_name
What it does:
- Reads the tech spec
- Extracts: name, base_url, auth method, amount format, content type
- Lists all supported flows with HTTP methods and endpoints
- Detects pre-auth flows:
Pre-Auth Flow (marker in connector_flow.rs) |
Detect when... |
| ServerAuthenticationToken |
OAuth/token auth (POST /login, /oauth/token) |
| CreateOrder |
Order/intent required before payment |
| CreateConnectorCustomer |
Customer object required before payment |
| PaymentMethodToken |
Tokenization required before authorize |
| ServerSessionAuthenticationToken |
Session init required before payment |
Marker names are the structs in crates/types-traits/domain_types/src/connector_flow.rs.
There is no CreateAccessToken / CreateSessionToken / PaymentAccessToken /
PaymentSessionToken anywhere in crates/ — use the names above verbatim.
Outputs: connector config, list of flows, list of pre-auth flows
Gate (HARD STOP — no exceptions):
If tech spec missing → STOP IMMEDIATELY. Do NOT proceed to Step 2.
Tell the user: "No tech spec found for {ConnectorName}. Please either:
(1) Run the generate-tech-spec skill first, or
(2) Provide the tech spec file manually at grace/rulesbook/codegen/references/{connector_name}/technical_specification.md."
Do NOT attempt to infer API details from any other source. A tech spec is mandatory.
Step 2: Foundation Setup (Subagent)
Subagent prompt: references/subagent-prompts.md → Subagent 2
Inputs: connector_name, base_url, production_base_url (optional), auth_method, amount_type (from Step 1)
What it does:
- Runs
.skills/new-connector/scripts/add_connector.sh {connector_name} {base_url} --force -y
(that path is a symlink to the real script, grace/rulesbook/codegen/add_connector.sh; either
path works, there is no scripts/add_connector.sh at the repo root)
- add
--production-url {production_base_url} when the live URL differs from the sandbox base URL
- add
--flows {Flow1},{Flow2},... only when this connector needs suites beyond the six core
flows; see "specs.json" below
- Verifies
cargo build --package connector-integration passes
- Checks UCS conventions (RouterDataV2, generic struct, domain_types imports)
- Sets up
create_amount_converter_wrapper! macro
- Implements
ConnectorCommon trait (id, content_type, base_url, auth_header, error_response)
- VERIFIES the base trait markers the scaffold already emitted -- it does NOT add them.
add_connector.sh writes ConnectorServiceTrait<T>, ValidationTrait, IncomingWebhook,
VerifyRedirectResponse and SourceVerification impls, and connector.rs.template
writes BodyDecoding. Writing any of them a second time is a conflicting implementation
(E0119), not a no-op. SourceVerification and BodyDecoding are NON-generic traits
(interfaces/src/verification.rs, interfaces/src/decode.rs): one impl per connector,
never one per flow -- a per-flow impl<T> SourceVerification<Flow, Data, Req, Resp> is
E0107. Working exemplar: connectors/travelhub.rs:175
- The scaffold script auto-registers the connector's base URLs in
config/superposition.toml
(dimension enum + sandbox/production overrides) and adds the URL-patching arm in types.rs
Connectors::patch_connector_urls — the subagent verifies both landed
- The scaffold script also writes
crates/internal/integration-tests/src/connector_specs/{connector_name}/specs.json, which the
ungated CI check cargo run --all-features --bin check_connector_specs requires. It seeds
supported_suites from --flows, defaulting to the six core flows
(Authorize,PSync,Capture,Void,Refund,RSync), and merges into an existing file rather than
overwriting it. Pass --flows only to seed a different set, e.g.
--flows Authorize,PSync,Capture,Void,Refund,RSync,SetupMandate.
--flows takes check_connector_specs flow names, NOT the trait names --list-flows prints —
the accepted vocabulary is the flow_to_suite table in the script (Authorize, PSync, Capture,
Void, Refund, RSync, SetupMandate, RepeatPayment, MandateRevoke, CreateConnectorCustomer,
GetConnectorCustomer, PaymentMethodToken, PaymentMethodEligibility, ServerAuthenticationToken,
ClientAuthenticationToken, ServerSessionAuthenticationToken, PreAuthenticate, Authenticate,
PostAuthenticate, CreateOrder, IncrementalAuthorization). An unrecognised name aborts the run.
Outputs: scaffold created, superposition URLs registered + URL patching wired,
crates/internal/integration-tests/src/connector_specs/{connector_name}/specs.json written, build passing, files list
Gate: Build must pass before proceeding.
Step 3: Flow Implementation (MANDATORY subagent per flow, sequential)
CRITICAL: You MUST delegate each flow to a subagent. Do NOT implement code yourself.
Read the subagent prompt from references/subagent-prompts.md → Subagent 3, fill in the
variables ({ConnectorName}, {FlowName}, tech spec path), and spawn a subagent for EACH flow.
Wait for each subagent to complete before spawning the next.
Detailed procedure: references/flow-implementation-guide.md
Per-flow patterns: references/flow-patterns/{flow}.md
Macro reference: references/macro-reference.md
Execution order (strict sequential — spawn one subagent per flow, wait for completion):
Pre-auth flows (only if detected in Step 1):
ServerAuthenticationToken → ServerSessionAuthenticationToken → CreateOrder →
CreateConnectorCustomer → PaymentMethodToken
Ordering notes: ServerAuthenticationToken and ServerSessionAuthenticationToken have NO
prerequisite (both suite specs under
crates/internal/integration-tests/src/global_suites/MerchantAuthenticationService_* have an
empty depends_on), so they go first and neither requires the other. PaymentMethodToken
depends on CreateConnectorCustomer — NOT on Authorize; tokenization runs before Authorize,
not after it.
Core flows (always):
Authorize → PSync → Capture → Refund → RSync → Void
Each flow subagent does:
- Reads tech spec for this flow's endpoint details
- Reads
references/flow-patterns/{flow}.md for patterns
- Removes the flow's marker name from the
not_implemented: [...] list in the
macro_connector_flow_status_impls! invocation. Do this FIRST. That macro emits both
the marker-trait impl and a stub ConnectorIntegrationV2 impl for every flow it lists,
so leaving the name there while adding your own is a double E0119
- Adds flow to
create_all_prerequisites! with correct types
- Adds
macro_connector_implementation! block
- Creates request/response types + TryFrom impls in transformers.rs
- Adds the flow's trait marker implementation -- now that step 3 freed it (marker names
are not uniform; the table is in
references/flow-implementation-guide.md)
- Runs
cargo build --package connector-integration
- Reports SUCCESS or FAILED
The macro nobody remembers
crates/integrations/connector-integration/src/connectors/macros.rs defines three macros
beyond the two above. All 111 connector files at HEAD invoke the first one.
| Macro |
Where |
What it takes |
macro_connector_flow_status_impls! |
macros.rs ~:1827 |
connector:, generic_type:, [<bounds>], not_implemented: [...], not_supported: [...] (either list may be omitted) |
macro_connector_local_flow_implementation! |
macros.rs ~:2425 |
flows with no outbound HTTP call |
macro_connector_payout_implementation! |
macros.rs ~:1448 |
connector:, generic_type:, [<bounds>] -- payout flow stubs |
A real invocation to copy (connectors/travelhub.rs:455):
macros::macro_connector_flow_status_impls!(
connector: Travelhub,
generic_type: T,
[PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],
not_implemented: [
Accept, ClientAuthenticationToken, CreateConnectorCustomer, GetConnectorCustomer,
DefendDispute, MandateRevoke, Authenticate, IncrementalAuthorization, CreateOrder,
PostAuthenticate, PreAuthenticate, PaymentMethodToken, VoidPC, RepeatPayment,
ServerAuthenticationToken, ServerSessionAuthenticationToken, SetupMandate,
SubmitEvidence
],
not_supported: [
VoidPostRefund,
],
);
Read the macro's first matcher arm before inventing argument keys.
Key type reference (full table in references/flow-implementation-guide.md):
| Flow |
FlowData |
RequestData |
ResponseData |
T? |
| Authorize |
PaymentFlowData |
PaymentsAuthorizeData<T> |
PaymentsResponseData |
Yes |
| PSync |
PaymentFlowData |
PaymentsSyncData |
PaymentsResponseData |
No |
| Capture |
PaymentFlowData |
PaymentsCaptureData |
PaymentsResponseData |
No |
| Void |
PaymentFlowData |
PaymentVoidData |
PaymentsResponseData |
No |
| Refund |
RefundFlowData |
RefundsData |
RefundsResponseData |
No |
| RSync |
RefundFlowData |
RefundSyncData |
RefundsResponseData |
No |
Step 4: gRPC Testing (MANDATORY subagent)
CRITICAL: You MUST delegate testing to a subagent. Do NOT run grpcurl yourself.
Subagent prompt: references/subagent-prompts.md → Subagent 4
Full testing guide: references/grpc-testing-guide.md
Inputs: connector_name, list of implemented flows, creds.json
What it does:
- Starts gRPC server (
cargo run --bin grpc-server)
- Loads credentials from
creds.json
- Tests each flow via grpcurl against the correct service/method
- Validates: status 2xx, no errors, correct status value
- If test fails: reads server logs, fixes code, rebuilds, retests
Key gRPC service mapping (full table in testing guide):
| Flow |
gRPC Method |
| Authorize |
types.PaymentService/Authorize |
| PSync |
types.PaymentService/Get |
| Capture |
types.PaymentService/Capture |
| Void |
types.PaymentService/Void |
| Refund |
types.PaymentService/Refund |
| RSync |
types.RefundService/Get |
Anti-loop safeguards: 3-strike rule, max 7 iterations, must change code between retries.
Gate: All flows must pass before proceeding.
Step 5: Quality Review (MANDATORY subagent)
CRITICAL: You MUST delegate quality review to a subagent. Do NOT review yourself.
Subagent prompt: references/subagent-prompts.md → Subagent 5
Checklist: references/quality-checklist.md
What it does:
- Architecture compliance: no RouterData (non-V2), no hyperswitch_domain_models
- Status mapping: no hardcoded statuses outside match arms;
#[serde(other)] Unknown on the
wire enum AND no _ => in the status-mapping match
- Code quality: no unwrap(), no None-hardcoded fields, descriptive errors,
NO_ERROR_CODE/NO_ERROR_MESSAGE instead of unwrap_or_default()
- Macro completeness: every implemented flow in both
create_all_prerequisites! and
macro_connector_implementation!, with its marker name REMOVED from
macro_connector_flow_status_impls!'s not_implemented list; every unimplemented flow
still listed there; no duplicate base-trait impls (E0119)
- Error types: only the five real
ConnectorError variants
(ResponseDeserializationFailed, ResponseHandlingFailed, UnexpectedResponseError,
IntegrityCheckFailed, ConnectorErrorResponse) — the first four carry a
context: ResponseTransformationErrorContext, while ConnectorErrorResponse wraps a
Box<ErrorResponse>; request-side failures use IntegrationError
- Naming conventions: {ConnectorName}{Flow}Request/Response pattern
- Final build:
cargo build --package connector-integration
Outputs: PASS with 0 violations, or FAIL with list of violations to fix.
The subagent runs the Pre-Flight Gate (references/quality-checklist.md §15) and
reports its output verbatim, not a summary. Each of the six items has a mechanical check;
"reviewed and looks fine" is a FAIL. The one most often waved through is §15.5 — PSync
does not re-derive a lookup key it could have read. The naive form of that check ("does
PSync read the carrier?") passes on a read that ends in unwrap_or(..) or .ok(), which
is exactly the bug: it syncs the wrong transaction the moment the carrier is absent. A
missing carrier must be IntegrationError::MissingRequiredField.
Step 6: Certification (MANDATORY — orchestrator-run; this is where "done" lives)
No subagent prompt exists for this step; the orchestrator runs it after Step 5 reports PASS.
Full reference: .skills/_shared/references/certification.md
(references/ has no symlink for this file yet — use the _shared path.)
cargo build is step 1 of 8, not the finish line. Since 2026-08-31 (75079740f)
connector certification is merge-blocking. A branch that compiles, lints clean and
answers grpcurl by hand can still be unmergeable, and the three mechanisms that block it
fail for different reasons:
| Mechanism |
Scope |
Escape hatch |
cargo run --bin check_connector_specs (Compilation Check job) |
every connector, every run |
none |
.github/scripts/verify-new-connectors.sh (Run Tests job) |
connectors added by this PR |
alpha_connectors.json + a reason |
.github/scripts/certify-connectors.sh (Run Tests job) |
connectors this PR touched |
merge-base arbitration |
A connector counts as new when connector_specs/<name>/ did not exist at the PR's base
commit. Splitting the PR does not change that. Every connector this skill produces is new.
Definition of done — the orchestrator does not report success until all eight hold:
cargo build --package connector-integration — zero errors, zero warnings
cargo run --bin check_connector_specs prints All checks passed. OK.
(Run it locally. Phase 1 exits 1 when a file under connectors/ has no matching
connector_specs/<name>/ directory, so a scaffold without specs.json cannot merge.)
specs.json trimmed to what is actually implemented. It is a certification claim,
not a scaffold artifact: a suite declared but not implemented fails against the sandbox;
a flow implemented but not declared fails check_connector_specs Phase 2. If it declares
EventService/HandleEvent, a real captured webhook_payload.json sits next to it
(Phase 2b) — do not fabricate one
- Certification decided in writing: either the connector has CI credentials and every
declared scenario passes
./scripts/run-tests --connector <name> --interface grpc --report, or an
alpha_connectors.json entry with a specific, non-empty reason. A bare {} entry
is exit 1 for a new connector. Listing it posts a public "merging without live sandbox
proof" comment on the PR; removing the name later is a promotion that pulls the
connector into the full sweep, so add_connector.sh deliberately never edits that file —
add the entry by hand
- Proto ordinal re-checked against
origin/main after the last commit (someone may have
merged an enum value first — renumber if the max is >= yours)
- Superposition URLs +
Connectors::patch_connector_urls verified
(cargo test -p grpc-server --all-features --test test_superposition_config)
- Pre-Flight Gate clean (
references/quality-checklist.md §15)
- Evidence regenerated against the branch head after the last fix commit. Captures
from an earlier revision are evidence for code that is not being merged
PR body requirements (references/quality-checklist.md §16). GRACE does not write
tests — that stands — so the PR body is the only place a human learns what needs one. Any
novel algorithmic logic in the diff (signing/HMAC, hashing, checksum, custom amount
encoding, a timestamp or nonce format that feeds a signature, bespoke serialization) is
listed line by line with the spec section it implements and a concrete input → expected
output, so a reviewer can add the known-answer test. Also state the certification status
and paste the check_connector_specs output.
"Out of scope" is not "do not build it." add_connector.sh's is_out_of_scope_flow()
lists flows excluded from certification. The Rust source it mirrors
(OUT_OF_SCOPE_FLOWS in crates/internal/integration-tests/src/bin/check_connector_specs.rs)
says why: "Each is a coverage gap, not a decision that it should never be covered."
VoidPC is on that list and is implemented by 14 connectors. Implement what the tech spec
calls for; it simply contributes no suite to supported_suites.
Fork PRs skip certification entirely (the Run Tests job's RUN_TESTS is false when
head.repo != base.repo). A green fork PR is not a certified connector — it is certified in
the merge queue, where merge_group turns the gate back on.
Reference Index
| Path |
Contents |
references/subagent-prompts.md |
Full copy-paste prompts for all 5 subagents |
references/flow-implementation-guide.md |
3-part flow procedure, type table (17 flows), per-flow subagent prompt |
references/grpc-testing-guide.md |
gRPC service map, grpcurl templates, test validation, testing subagent prompt |
references/macro-reference.md |
Both core macros, parameters, content types, generic rules |
references/type-system.md |
Core imports, type paths, domain_types module structure |
references/utility-functions.md |
Error handling, card formatting, amount conversion helpers |
references/quality-checklist.md |
Pre-submission checklist, §15 Pre-Flight Gate, §16 PR-body disclosure, common mistakes |
.skills/_shared/references/certification.md |
The merge-blocking certification gate: check_connector_specs phases, verify-new-connectors.sh fail paths, alpha_connectors.json, definition of done |
references/flow-patterns/*.md |
Per-flow: authorize, psync, capture, refund, rsync, void |
.skills/new-connector/scripts/add_connector.sh |
Scaffold script that generates initial connector files (symlink to grace/rulesbook/codegen/add_connector.sh) |
1---2name: new-connector3description: Implements a new payment connector from scratch in the connector-service (UCS) Rust codebase. Creates connector foundation and implements all 6 core payment flows (Authorize, PSync, Capture, Refund, RSync, Void). Use when integrating a new payment gateway that does not yet exist. Requires a technical specification at grace/rulesbook/codegen/references/{connector_name}/technical_specification.md.4license: Apache-2.05---67# New Connector Implementation89## Overview1011This skill produces a complete payment connector in the UCS Rust codebase.1213**MANDATORY SUBAGENT DELEGATION: You are the orchestrator. You MUST delegate every step14to a subagent using the prompts in `references/subagent-prompts.md`. Do NOT implement15code, run tests, or review quality yourself. Spawn subagents and coordinate their outputs.**1617**Output:**18- Main connector file with macro-based flow implementations19- Transformers module with request/response types and conversions20- Registration in the connector registry21- All 6 core flows + any required pre-auth flows22- gRPC tested end-to-end23- Certification manifest (`connector_specs/{connector_name}/specs.json`) that survives the24 merge-blocking CI gate — see Step 62526**Prerequisites:**27- Tech spec at `grace/rulesbook/codegen/references/{connector_name}/technical_specification.md`28- Rust toolchain with `cargo`2930## Project Structure3132| Purpose | Path |33|---------|------|34| Main connector file | `crates/integrations/connector-integration/src/connectors/{connector_name}.rs` |35| Transformers module | `crates/integrations/connector-integration/src/connectors/{connector_name}/transformers.rs` |36| Connector registry | `crates/integrations/connector-integration/src/connectors.rs` |37| Enum definitions | `crates/common/common_enums/src/enums.rs` |38| Domain utilities | `crates/types-traits/domain_types/src/utils.rs` |39| Macro definitions | `crates/integrations/connector-integration/src/connectors/macros.rs` |40| Superposition URLs | `config/superposition.toml` |41| URL patching (`Connectors::apply`) | `crates/types-traits/domain_types/src/types.rs` |4243## Critical Conventions4445These rules apply to ALL subagents. Include them in every subagent prompt.4647- Use `RouterDataV2` (NEVER `RouterData`), `ConnectorIntegrationV2` (NEVER `ConnectorIntegration`)48- Import from `domain_types` (NEVER `hyperswitch_domain_models`)49- Connector struct MUST be generic: `ConnectorName<T>`50- NEVER hardcode status values -- always map from connector response via `From`/`TryFrom`51- Use macros (`create_all_prerequisites!` + `macro_connector_implementation!`) for all flows52- Flows you do NOT implement are stubbed by `macro_connector_flow_status_impls!`53 (`not_implemented: [...]` / `not_supported: [...]`) -- see "The macro nobody remembers" below54- Check `references/utility-functions.md` before implementing custom helpers55- No `unwrap()`, no fields hardcoded to `None`, no unnecessary `.clone()`56- Auth data accessed via `req.connector_config` (NOT `connector_auth_type`, which was57 deleted from `RouterDataV2`). `get_auth_header` takes `&ConnectorSpecificConfig`58 (`crates/types-traits/interfaces/src/api.rs:25`); copy the idiom from a recent connector59 such as `connectors/travelhub.rs`60- `build_error_response` takes THREE parameters --61 `(res: Response, _event_builder: Option<&mut events::Event>, _connector_config: &ConnectorSpecificConfig)`62 (`interfaces/src/api.rs:50`). The event type is `events::Event`; there is no `ConnectorEvent`63 in this crate, and `events::Event` has no `set_error_response_body` method. The same64 third parameter applies to `get_error_response_v2` and `get_5xx_error_response`65- `ErrorResponse` has 13 fields and DOES implement `Default`66 (`domain_types/src/router_data.rs`), so prefer `..Default::default()` over listing them.67 `attempt_status` is `Option<FlowStatus>`, not `Option<AttemptStatus>` -- wrap it, e.g.68 `Some(FlowStatus::Payment(AttemptStatus::Failure))`, and never force a terminal status on69 the shared error path (exemplar: `connectors/flywire.rs:362-370`; minimal form70 `connectors/noon.rs:499-512`)71- `PaymentsResponseData::TransactionResponse` (11 fields) and `RefundsResponseData`72 (4 fields) are read from `domain_types/src/connector_types.rs`. Enum struct-variants have73 no functional-update syntax, so every omitted field is E0063 -- list them all74- Error codes/messages: `.unwrap_or_else(|| NO_ERROR_CODE.to_string())` and75 `NO_ERROR_MESSAGE`, from `crates/common/common_utils/src/consts.rs`. Never76 `error_code.unwrap_or_default()`77- Amount unit: read the vendor spec and match its wire format. The five types in78 `crates/common/common_utils/src/types.rs` are `MinorUnit`, `StringMinorUnit`,79 `StringMajorUnit`, `FloatMajorUnit`, `StringTwoDecimalUnit`. There is no safe default --80 `rg -o "amount_type: \w+" crates/integrations/connector-integration/src/connectors/*.rs`81 at HEAD splits ~10/10/9/4 across StringMajorUnit / MinorUnit / StringMinorUnit / FloatMajorUnit82- Status enums need `#[serde(other)] Unknown` at the DESERIALIZATION layer; the83 status-mapping `match` must stay exhaustive over named variants (no `_ =>` there).84 Reviewers require both halves85- An in-band failure returned with HTTP 2xx must produce `Err(ErrorResponse { .. })`,86 branching on a success predicate -- see `utils::is_payment_failure` in87 `crates/types-traits/domain_types/src/utils.rs`88- Connector base URLs MUST be registered in `config/superposition.toml` (dimension enum + sandbox &89 production `connector_base_url` overrides) AND the connector wired into90 `Connectors::patch_connector_urls` in `crates/types-traits/domain_types/src/types.rs` for dynamic91 URL patching. The scaffold script (`add_connector.sh`) now does this automatically — verify it92 landed, and pass `--production-url` when the live URL differs from the sandbox base URL.9394---9596## Workflow: Orchestrator Sequence9798Each step below is an independent subagent. The orchestrator delegates each step,99waits for completion, and passes outputs to the next step.100101**Full subagent prompts:** `references/subagent-prompts.md`102103### Step 1: Tech Spec Validation (Subagent)104105> **Subagent prompt:** `references/subagent-prompts.md` → Subagent 1106107**Inputs:** connector_name108109**What it does:**110- Reads the tech spec111- Extracts: name, base_url, auth method, amount format, content type112- Lists all supported flows with HTTP methods and endpoints113- Detects pre-auth flows:114115| Pre-Auth Flow (marker in `connector_flow.rs`) | Detect when... |116|---------------|---------------|117| ServerAuthenticationToken | OAuth/token auth (POST /login, /oauth/token) |118| CreateOrder | Order/intent required before payment |119| CreateConnectorCustomer | Customer object required before payment |120| PaymentMethodToken | Tokenization required before authorize |121| ServerSessionAuthenticationToken | Session init required before payment |122123> Marker names are the structs in `crates/types-traits/domain_types/src/connector_flow.rs`.124> There is no `CreateAccessToken` / `CreateSessionToken` / `PaymentAccessToken` /125> `PaymentSessionToken` anywhere in `crates/` — use the names above verbatim.126127**Outputs:** connector config, list of flows, list of pre-auth flows128129**Gate (HARD STOP — no exceptions):**130If tech spec missing → **STOP IMMEDIATELY. Do NOT proceed to Step 2.**131Tell the user: "No tech spec found for {ConnectorName}. Please either:132(1) Run the `generate-tech-spec` skill first, or133(2) Provide the tech spec file manually at `grace/rulesbook/codegen/references/{connector_name}/technical_specification.md`."134Do NOT attempt to infer API details from any other source. A tech spec is mandatory.135136---137138### Step 2: Foundation Setup (Subagent)139140> **Subagent prompt:** `references/subagent-prompts.md` → Subagent 2141142**Inputs:** connector_name, base_url, production_base_url (optional), auth_method, amount_type (from Step 1)143144**What it does:**145- Runs `.skills/new-connector/scripts/add_connector.sh {connector_name} {base_url} --force -y`146 (that path is a symlink to the real script, `grace/rulesbook/codegen/add_connector.sh`; either147 path works, there is no `scripts/add_connector.sh` at the repo root)148 - add `--production-url {production_base_url}` when the live URL differs from the sandbox base URL149 - add `--flows {Flow1},{Flow2},...` only when this connector needs suites beyond the six core150 flows; see "specs.json" below151- Verifies `cargo build --package connector-integration` passes152- Checks UCS conventions (RouterDataV2, generic struct, domain_types imports)153- Sets up `create_amount_converter_wrapper!` macro154- Implements `ConnectorCommon` trait (id, content_type, base_url, auth_header, error_response)155- **VERIFIES** the base trait markers the scaffold already emitted -- it does NOT add them.156 `add_connector.sh` writes `ConnectorServiceTrait<T>`, `ValidationTrait`, `IncomingWebhook`,157 `VerifyRedirectResponse` and `SourceVerification` impls, and `connector.rs.template`158 writes `BodyDecoding`. Writing any of them a second time is a conflicting implementation159 (**E0119**), not a no-op. `SourceVerification` and `BodyDecoding` are NON-generic traits160 (`interfaces/src/verification.rs`, `interfaces/src/decode.rs`): one impl per connector,161 never one per flow -- a per-flow `impl<T> SourceVerification<Flow, Data, Req, Resp>` is162 **E0107**. Working exemplar: `connectors/travelhub.rs:175`163- The scaffold script auto-registers the connector's base URLs in `config/superposition.toml`164 (dimension enum + sandbox/production overrides) and adds the URL-patching arm in `types.rs`165 `Connectors::patch_connector_urls` — the subagent verifies both landed166- The scaffold script also writes167 `crates/internal/integration-tests/src/connector_specs/{connector_name}/specs.json`, which the168 ungated CI check `cargo run --all-features --bin check_connector_specs` requires. It seeds169 `supported_suites` from `--flows`, defaulting to the six core flows170 (`Authorize,PSync,Capture,Void,Refund,RSync`), and merges into an existing file rather than171 overwriting it. Pass `--flows` only to seed a different set, e.g.172 `--flows Authorize,PSync,Capture,Void,Refund,RSync,SetupMandate`.173 `--flows` takes `check_connector_specs` flow names, NOT the trait names `--list-flows` prints —174 the accepted vocabulary is the `flow_to_suite` table in the script (Authorize, PSync, Capture,175 Void, Refund, RSync, SetupMandate, RepeatPayment, MandateRevoke, CreateConnectorCustomer,176 GetConnectorCustomer, PaymentMethodToken, PaymentMethodEligibility, ServerAuthenticationToken,177 ClientAuthenticationToken, ServerSessionAuthenticationToken, PreAuthenticate, Authenticate,178 PostAuthenticate, CreateOrder, IncrementalAuthorization). An unrecognised name aborts the run.179180**Outputs:** scaffold created, superposition URLs registered + URL patching wired,181`crates/internal/integration-tests/src/connector_specs/{connector_name}/specs.json` written, build passing, files list182183**Gate:** Build must pass before proceeding.184185---186187### Step 3: Flow Implementation (MANDATORY subagent per flow, sequential)188189> **CRITICAL: You MUST delegate each flow to a subagent. Do NOT implement code yourself.**190> Read the subagent prompt from `references/subagent-prompts.md` → Subagent 3, fill in the191> variables ({ConnectorName}, {FlowName}, tech spec path), and spawn a subagent for EACH flow.192> Wait for each subagent to complete before spawning the next.193194> **Detailed procedure:** `references/flow-implementation-guide.md`195> **Per-flow patterns:** `references/flow-patterns/{flow}.md`196> **Macro reference:** `references/macro-reference.md`197198**Execution order** (strict sequential — spawn one subagent per flow, wait for completion):1992001. Pre-auth flows (only if detected in Step 1):201 ServerAuthenticationToken → ServerSessionAuthenticationToken → CreateOrder →202 CreateConnectorCustomer → PaymentMethodToken203204 Ordering notes: `ServerAuthenticationToken` and `ServerSessionAuthenticationToken` have NO205 prerequisite (both suite specs under206 `crates/internal/integration-tests/src/global_suites/MerchantAuthenticationService_*` have an207 empty `depends_on`), so they go first and neither requires the other. `PaymentMethodToken`208 depends on `CreateConnectorCustomer` — NOT on Authorize; tokenization runs before Authorize,209 not after it.2102112. Core flows (always):212 Authorize → PSync → Capture → Refund → RSync → Void213214**Each flow subagent does:**2151. Reads tech spec for this flow's endpoint details2162. Reads `references/flow-patterns/{flow}.md` for patterns2173. **Removes the flow's marker name from the `not_implemented: [...]` list in the218 `macro_connector_flow_status_impls!` invocation.** Do this FIRST. That macro emits both219 the marker-trait impl and a stub `ConnectorIntegrationV2` impl for every flow it lists,220 so leaving the name there while adding your own is a double **E0119**2214. Adds flow to `create_all_prerequisites!` with correct types2225. Adds `macro_connector_implementation!` block2236. Creates request/response types + TryFrom impls in transformers.rs2247. Adds the flow's trait marker implementation -- now that step 3 freed it (marker names225 are not uniform; the table is in `references/flow-implementation-guide.md`)2268. Runs `cargo build --package connector-integration`2279. Reports SUCCESS or FAILED228229### The macro nobody remembers230231`crates/integrations/connector-integration/src/connectors/macros.rs` defines three macros232beyond the two above. All 111 connector files at HEAD invoke the first one.233234| Macro | Where | What it takes |235|-------|-------|---------------|236| `macro_connector_flow_status_impls!` | `macros.rs` ~:1827 | `connector:`, `generic_type:`, `[<bounds>]`, `not_implemented: [...]`, `not_supported: [...]` (either list may be omitted) |237| `macro_connector_local_flow_implementation!` | `macros.rs` ~:2425 | flows with no outbound HTTP call |238| `macro_connector_payout_implementation!` | `macros.rs` ~:1448 | `connector:`, `generic_type:`, `[<bounds>]` -- payout flow stubs |239240A real invocation to copy (`connectors/travelhub.rs:455`):241242```rust243macros::macro_connector_flow_status_impls!(244 connector: Travelhub,245 generic_type: T,246 [PaymentMethodDataTypes + Debug + Sync + Send + 'static + Serialize],247 not_implemented: [248 Accept, ClientAuthenticationToken, CreateConnectorCustomer, GetConnectorCustomer,249 DefendDispute, MandateRevoke, Authenticate, IncrementalAuthorization, CreateOrder,250 PostAuthenticate, PreAuthenticate, PaymentMethodToken, VoidPC, RepeatPayment,251 ServerAuthenticationToken, ServerSessionAuthenticationToken, SetupMandate,252 SubmitEvidence253 ],254 not_supported: [255 VoidPostRefund,256 ],257);258```259260Read the macro's first matcher arm before inventing argument keys.261262**Key type reference** (full table in `references/flow-implementation-guide.md`):263264| Flow | FlowData | RequestData | ResponseData | T? |265|------|----------|-------------|--------------|-----|266| Authorize | PaymentFlowData | PaymentsAuthorizeData\<T\> | PaymentsResponseData | Yes |267| PSync | PaymentFlowData | PaymentsSyncData | PaymentsResponseData | No |268| Capture | PaymentFlowData | PaymentsCaptureData | PaymentsResponseData | No |269| Void | PaymentFlowData | PaymentVoidData | PaymentsResponseData | No |270| Refund | RefundFlowData | RefundsData | RefundsResponseData | No |271| RSync | RefundFlowData | RefundSyncData | RefundsResponseData | No |272273---274275### Step 4: gRPC Testing (MANDATORY subagent)276277> **CRITICAL: You MUST delegate testing to a subagent. Do NOT run grpcurl yourself.**278> **Subagent prompt:** `references/subagent-prompts.md` → Subagent 4279> **Full testing guide:** `references/grpc-testing-guide.md`280281**Inputs:** connector_name, list of implemented flows, creds.json282283**What it does:**2841. Starts gRPC server (`cargo run --bin grpc-server`)2852. Loads credentials from `creds.json`2863. Tests each flow via grpcurl against the correct service/method2874. Validates: status 2xx, no errors, correct status value2885. If test fails: reads server logs, fixes code, rebuilds, retests289290**Key gRPC service mapping** (full table in testing guide):291292| Flow | gRPC Method |293|------|-------------|294| Authorize | `types.PaymentService/Authorize` |295| PSync | `types.PaymentService/Get` |296| Capture | `types.PaymentService/Capture` |297| Void | `types.PaymentService/Void` |298| Refund | `types.PaymentService/Refund` |299| RSync | `types.RefundService/Get` |300301**Anti-loop safeguards:** 3-strike rule, max 7 iterations, must change code between retries.302303**Gate:** All flows must pass before proceeding.304305---306307### Step 5: Quality Review (MANDATORY subagent)308309> **CRITICAL: You MUST delegate quality review to a subagent. Do NOT review yourself.**310> **Subagent prompt:** `references/subagent-prompts.md` → Subagent 5311> **Checklist:** `references/quality-checklist.md`312313**What it does:**3141. Architecture compliance: no RouterData (non-V2), no hyperswitch_domain_models3152. Status mapping: no hardcoded statuses outside match arms; `#[serde(other)] Unknown` on the316 wire enum AND no `_ =>` in the status-mapping match3173. Code quality: no unwrap(), no None-hardcoded fields, descriptive errors,318 `NO_ERROR_CODE`/`NO_ERROR_MESSAGE` instead of `unwrap_or_default()`3194. Macro completeness: every implemented flow in both `create_all_prerequisites!` and320 `macro_connector_implementation!`, with its marker name REMOVED from321 `macro_connector_flow_status_impls!`'s `not_implemented` list; every unimplemented flow322 still listed there; no duplicate base-trait impls (E0119)3235. Error types: only the five real `ConnectorError` variants324 (`ResponseDeserializationFailed`, `ResponseHandlingFailed`, `UnexpectedResponseError`,325 `IntegrityCheckFailed`, `ConnectorErrorResponse`) — the first four carry a326 `context: ResponseTransformationErrorContext`, while `ConnectorErrorResponse` wraps a327 `Box<ErrorResponse>`; request-side failures use `IntegrationError`3286. Naming conventions: {ConnectorName}{Flow}Request/Response pattern3297. Final build: `cargo build --package connector-integration`330331**Outputs:** PASS with 0 violations, or FAIL with list of violations to fix.332333The subagent runs the **Pre-Flight Gate** (`references/quality-checklist.md` §15) and334reports its output verbatim, not a summary. Each of the six items has a mechanical check;335"reviewed and looks fine" is a FAIL. The one most often waved through is §15.5 — *PSync336does not re-derive a lookup key it could have read*. The naive form of that check ("does337PSync read the carrier?") passes on a read that ends in `unwrap_or(..)` or `.ok()`, which338is exactly the bug: it syncs the wrong transaction the moment the carrier is absent. A339missing carrier must be `IntegrationError::MissingRequiredField`.340341---342343### Step 6: Certification (MANDATORY — orchestrator-run; this is where "done" lives)344345No subagent prompt exists for this step; the orchestrator runs it after Step 5 reports PASS.346347> **Full reference:** `.skills/_shared/references/certification.md`348> (`references/` has no symlink for this file yet — use the `_shared` path.)349350**`cargo build` is step 1 of 8, not the finish line.** Since 2026-08-31 (`75079740f`)351connector certification is **merge-blocking**. A branch that compiles, lints clean and352answers grpcurl by hand can still be unmergeable, and the three mechanisms that block it353fail for different reasons:354355| Mechanism | Scope | Escape hatch |356|---|---|---|357| `cargo run --bin check_connector_specs` (Compilation Check job) | every connector, every run | none |358| `.github/scripts/verify-new-connectors.sh` (Run Tests job) | connectors **added by this PR** | `alpha_connectors.json` + a `reason` |359| `.github/scripts/certify-connectors.sh` (Run Tests job) | connectors this PR **touched** | merge-base arbitration |360361A connector counts as **new** when `connector_specs/<name>/` did not exist at the PR's base362commit. Splitting the PR does not change that. Every connector this skill produces is new.363364**Definition of done** — the orchestrator does not report success until all eight hold:3653661. `cargo build --package connector-integration` — zero errors, zero warnings3672. `cargo run --bin check_connector_specs` prints `All checks passed. OK.`368 *(Run it locally. Phase 1 exits 1 when a file under `connectors/` has no matching369 `connector_specs/<name>/` directory, so a scaffold without specs.json cannot merge.)*3703. `specs.json` **trimmed to what is actually implemented**. It is a certification claim,371 not a scaffold artifact: a suite declared but not implemented fails against the sandbox;372 a flow implemented but not declared fails `check_connector_specs` Phase 2. If it declares373 `EventService/HandleEvent`, a real captured `webhook_payload.json` sits next to it374 (Phase 2b) — do not fabricate one3754. Certification decided **in writing**: either the connector has CI credentials and every376 declared scenario passes377 `./scripts/run-tests --connector <name> --interface grpc --report`, or an378 `alpha_connectors.json` entry with a **specific, non-empty `reason`**. A bare `{}` entry379 is `exit 1` for a new connector. Listing it posts a public "merging without live sandbox380 proof" comment on the PR; removing the name later is a *promotion* that pulls the381 connector into the full sweep, so `add_connector.sh` deliberately never edits that file —382 add the entry by hand3835. Proto ordinal re-checked against `origin/main` after the last commit (someone may have384 merged an enum value first — renumber if the max is `>=` yours)3856. Superposition URLs + `Connectors::patch_connector_urls` verified386 (`cargo test -p grpc-server --all-features --test test_superposition_config`)3877. Pre-Flight Gate clean (`references/quality-checklist.md` §15)3888. Evidence regenerated **against the branch head** after the last fix commit. Captures389 from an earlier revision are evidence for code that is not being merged390391**PR body requirements** (`references/quality-checklist.md` §16). GRACE does not write392tests — that stands — so the PR body is the only place a human learns what needs one. Any393**novel algorithmic logic** in the diff (signing/HMAC, hashing, checksum, custom amount394encoding, a timestamp or nonce format that feeds a signature, bespoke serialization) is395**listed line by line with the spec section it implements and a concrete input → expected396output**, so a reviewer can add the known-answer test. Also state the certification status397and paste the `check_connector_specs` output.398399**"Out of scope" is not "do not build it."** `add_connector.sh`'s `is_out_of_scope_flow()`400lists flows excluded from *certification*. The Rust source it mirrors401(`OUT_OF_SCOPE_FLOWS` in `crates/internal/integration-tests/src/bin/check_connector_specs.rs`)402says why: *"Each is a coverage gap, not a decision that it should never be covered."*403`VoidPC` is on that list and is implemented by 14 connectors. Implement what the tech spec404calls for; it simply contributes no suite to `supported_suites`.405406**Fork PRs skip certification entirely** (the Run Tests job's `RUN_TESTS` is false when407`head.repo != base.repo`). A green fork PR is not a certified connector — it is certified in408the merge queue, where `merge_group` turns the gate back on.409410---411412## Reference Index413414| Path | Contents |415|------|----------|416| `references/subagent-prompts.md` | Full copy-paste prompts for all 5 subagents |417| `references/flow-implementation-guide.md` | 3-part flow procedure, type table (17 flows), per-flow subagent prompt |418| `references/grpc-testing-guide.md` | gRPC service map, grpcurl templates, test validation, testing subagent prompt |419| `references/macro-reference.md` | Both core macros, parameters, content types, generic rules |420| `references/type-system.md` | Core imports, type paths, domain_types module structure |421| `references/utility-functions.md` | Error handling, card formatting, amount conversion helpers |422| `references/quality-checklist.md` | Pre-submission checklist, §15 Pre-Flight Gate, §16 PR-body disclosure, common mistakes |423| `.skills/_shared/references/certification.md` | The merge-blocking certification gate: `check_connector_specs` phases, `verify-new-connectors.sh` fail paths, `alpha_connectors.json`, definition of done |424| `references/flow-patterns/*.md` | Per-flow: authorize, psync, capture, refund, rsync, void |425| `.skills/new-connector/scripts/add_connector.sh` | Scaffold script that generates initial connector files (symlink to `grace/rulesbook/codegen/add_connector.sh`) |