# Create a Referral via API

Build and submit an outbound co-sell referral to AWS, Microsoft (Azure), or Google Cloud (GCP) directly through the Fours API.

---

## Overview

This page is the developer companion to the [Outbound Referral](/cosell/referrals/outbound/) UI walkthrough. It documents the request payload for one endpoint:

```
POST /org/{orgId}/cosell/referral
```

- **Auth:** API key (`Authorization` header). See [Authentication](/get-started/).
- **Query param:** `forceCreate` (boolean, optional). When `true`, the referral is saved in Fours even if it fails validation or the partner rejects it, so you can fix and resubmit later. Default `false`.
- **Response:** `200` with an `OperationExecutionDetails` object (contains the `workflowID` of the submission). On validation failure you get `400` with an `info` array of per-field errors (see [Validate first](#validate-first)).

One endpoint serves all three clouds. The cloud is chosen by the top-level `partner` field, and the partner-specific opportunity goes in a matching slot under `info`. **Submission is automatic** — the create workflow fires immediately and pushes the referral to the partner. Do **not** call a sync afterwards for the initial submit. The resulting status is `PENDING_CREATE` on success or `CREATE_FAILED` if the partner API rejects it (to resubmit, send the referral back to this endpoint with its `id` — see [Response & next steps](#response--next-steps)).

:::note
If your org has the **referral approval process** enabled and the referral is not yet approved, a brand-new referral is saved as `DRAFT` and the workflow does **not** fire until it is approved in the Fours Console.
:::

## Try it in Postman

Download the [co-sell referral Postman collection](/postman/cosell-referrals.postman_collection.json) and its [environment template](/postman/sandbox.postman_environment.json), then import both into Postman. The collection is generated from the same API spec as the [API reference](/api/create-cosell-referral/) and carries the same request examples.

For AWS and for Azure (partner-led), it validates an opportunity, creates the referral, waits for the partner submission, lists referrals, updates a referral and waits for the update to reach the partner, previews a referral from a Salesforce opportunity, and deletes the referral it created. Each request's description explains its body.

Fill in the environment before you run it:

| Variable | What to set |
|---|---|
| `baseUrl` | `https://api.suger.cloud` (already set) |
| `orgId` | The ID of the organization to run against — a sandbox, see below |
| `apiKey` | An API key with all five of `cosell.read_referral`, `cosell.create_referral`, `cosell.edit_referral`, `cosell.delete_referral` and `operation.read`. A narrower key fails partway through with `403`. |
| `salesforceOpportunityId` | An opportunity ID from the organization's connected Salesforce, for the preview step |
| `awsApprovedReferralId` | An AWS referral that AWS has already approved. AWS refuses updates while an opportunity is under review, which every new one is, so the AWS update step edits this referral instead of the one it just created. |

:::caution
The collection is not a dry run, and it calls the production API. Every pass creates a real referral, submits it to AWS Partner Central and Microsoft Partner Center, and deletes it again, so point `orgId` at a sandbox organization. One change outlives the pass: the AWS update step overwrites the close date, next steps and expected customer spend of the referral in `awsApprovedReferralId`, and nothing restores them.
:::

## The envelope

Every request body has the same outer shape, regardless of cloud:

```jsonc
{
  "partner": "AWS",                        // "AWS" | "AZURE" | "GCP"
  "salesforceOpportunityId": "0065g...",   // optional: link to a CRM opportunity
  "salesforceAccountId": "0015g...",       // optional: link to the CRM account
  "info": {
    // exactly one slot, matching `partner` — see table below
  },
  "metaInfo": { /* optional */ },
  "approvalInfo": { /* optional, for the approval workflow */ }
}
```

:::note[Linking to Salesforce]
`salesforceOpportunityId` and `salesforceAccountId` are optional — set them to associate the referral with a CRM record so it shows up linked in the Fours Console and stays in sync. If you provide `salesforceOpportunityId` but leave `salesforceAccountId` blank, Fours automatically derives the account from that opportunity. Set `salesforceAccountId` explicitly only when you want to control or override which account is linked. This applies to all three clouds.
:::

The partner-specific opportunity lives in a different `info` slot for each cloud:

| `partner` | Send the opportunity under | Body type |
|---|---|---|
| `AWS`   | `info.aceOpportunityV2`              | AWS Partner Central opportunity |
| `AZURE` | `info.createMicrosoftReferralRequest`| Microsoft Partner Center referral |
| `GCP`   | `info.gcpOpportunityV2`              | Google Cloud opportunity |

:::note[Azure: what you send vs. what you get back]
On **create** you send `info.createMicrosoftReferralRequest`. Fours converts it internally and persists it as `info.microsoftReferralSet`, so `GET` responses return the referral under `microsoftReferralSet` — not `createMicrosoftReferralRequest`. Always build the create payload with `createMicrosoftReferralRequest`.
:::

### Two tiers of "required"

Throughout this page, required fields fall into two groups:

- **Required by Fours** — missing/invalid values return a `400` *before* anything reaches the cloud partner. These are the hard blockers.
- **Required by the cloud partner** — Fours may accept the payload, but AWS/Microsoft/Google will reject it. Fours' auto-enrich and auto-fix features try to fill some of these; the authoritative list is each cloud's own API reference, linked per section.

## Validate first

Before creating, dry-run the payload against the partner schema. Nothing is created or sent:

```
POST /org/{orgId}/cosell/referral/validate
```

The body names the partner and carries the partner-specific opportunity — the object you would
put in the envelope's `info` slot, not the whole envelope:

```jsonc
{
  "partner": "AWS",          // "AWS" | "GCP" | "AZURE" — a Fours partner (SUGER) referral can't be validated
  "opportunity": { /* what you'd send under info.aceOpportunityV2, info.gcpOpportunityV2,
                      or info.createMicrosoftReferralRequest */ },
  "referralId": "..."        // optional: only when validating a change to an existing referral
}
```

Pass `referralId` when you validate a change to a referral that already exists, so it is not
reported as a duplicate of itself. The answer is a `200` whether or not the opportunity passes, so
you can correct the payload without creating a `CREATE_FAILED` record:

```jsonc
{
  "valid": false,
  "errors": [
    { "field": "customer.account.industry", "message": "'Industry vertical' is required" },
    { "field": "relatedEntityIdentifiers.solutions", "message": "'Solutions' is required" }
  ],
  "duplicateOpenReferrals": [   // advisory; omitted when there are none
    { "referralId": "...", "title": "Acme Corp — Migration", "partner": "AWS",
      "status": "IN_REVIEW", "externalId": "O1234567" }
  ],
  "normalizedOpportunity": { /* the opportunity as the partner would receive it */ }
}
```

- **`valid` and `errors`** are the verdict. A `400` from this endpoint means the request itself is
  malformed — no `partner`, an unsupported partner, or an opportunity that can't be read.
- **`duplicateOpenReferrals`** lists still-open referrals for the same partner with the same
  title. It never changes `valid` and never blocks a create.
- **`normalizedOpportunity`** is the opportunity after the partner's validator has normalized it —
  what the partner would actually receive. Validators silently blank, shorten, or drop values they
  can't accept — on AWS, for example, an invalid AWS account ID is blanked, `AdditionalComments`
  is cut to 255 characters, and `SoftwareRevenue` is dropped for an ineligible partner type — so
  compare it with what you sent. For Azure, `valid` and `errors` come from strict validation,
  while `normalizedOpportunity` shows the payload after Fours' automatic corrections, which is
  what a create would submit.

If you create a referral without validating it first, the same problems come back on the create
call as a `400` with an `info` array of per-field errors:

```jsonc
{
  "code": "BAD_REQUEST",
  "message": "failed to validate ...",
  "info": [
    { "field": "customer.account.industry", "message": "'Industry vertical' is required" },
    { "field": "relatedEntityIdentifiers.solutions", "message": "'Solutions' is required" }
  ]
}
```

---

## AWS

Send the opportunity under `info.aceOpportunityV2`. Field keys use **PascalCase** (e.g. `Customer`, `LifeCycle`), matching the AWS Partner Central Selling API.

### Required by Fours

| Field (under `info.aceOpportunityV2`) | Rule |
|---|---|
| `Customer.Account.Industry` | Non-empty; from the AWS industry list |
| `Customer.Account.CompanyName` | Non-empty (truncated to 120 chars) |
| `Customer.Account.WebsiteUrl` | Valid URL — waived only when `NationalSecurity` is `"Yes"` |
| `Customer.Account.Address.CountryCode` | ISO 3166-1 alpha-2 |
| `Customer.Account.Address.PostalCode` | Must match the country's postal pattern |
| `Customer.Account.Address.StateOrRegion` | Required **only when `CountryCode` is `US`** |
| `PrimaryNeedsFromAws` | Array, at least one value. See [AWS field options](#aws-field-options) |
| `Project.CustomerBusinessProblem` | Non-empty, at least 20 characters (over 2000 is truncated) |
| `Project.ExpectedCustomerSpend[0].Amount` | A number above 0 once rounded to two decimal places. A `$` sign, US thousands commas, spaces and a trailing `USD` are removed first (`"$1,250 USD"` is sent as `"1250"`); any other text, including a comma that isn't a US thousands separator (`"1.000,50"`), is rejected. This is Fours' rule: AWS's published [ExpectedCustomerSpend](https://docs.aws.amazon.com/partner-central/latest/APIReference/API_ExpectedCustomerSpend.html) schema is less strict |
| `RelatedEntityIdentifiers.Solutions` | Array of your AWS Solution IDs, at least one. See [Where Solution IDs come from](#where-solution-ids-come-from) |
| `LifeCycle.TargetCloseDate` | `YYYY-MM-DD`, subject to the [close-date policy](#the-targetclosedate-policy) below |

### Conditionally blocked by Fours

These are rejected only when **present and invalid**. Omit one entirely and Fours' validator passes — then AWS rejects the submission and the referral lands in `CREATE_FAILED`. Send them.

| Field | Rule when present |
|---|---|
| `Project.CustomerUseCase` | Must be a valid use-case value |
| `Project.DeliveryModels` | Rejected only if **every** value is invalid; otherwise invalid entries are dropped |

### Also required by AWS (Fours does not block these)

Fours' validator passes **without** these, but they are flagged `requiredForCreation` in the AWS ACE schema — **AWS rejects the submission without them**, so Fours accepts the payload and the AWS step then fails as `CREATE_FAILED`. The example below includes all of them:

| Field | Notes |
|---|---|
| `OpportunityType` | You provide it: `Net New Business` \| `Flat Renewal` \| `Expansion`. Silently **cleared** if invalid, not rejected |
| `Project.Title` | You provide it |
| `Project.SalesActivities` | Fours defaults if omitted |
| `Origin` | You provide it: `Partner Referral` (the only valid value for `Catalog: AWS`) |
| `Marketing.Source` | Fours fills (`None`) |
| `PartnerOpportunityIdentifier` | Fours sets it to the referral ID |

So beyond Fours' hard-blocked list, the fields *you* must supply for AWS to accept are `Origin`, `OpportunityType`, `Project.Title`, `Project.CustomerUseCase`, and `Project.DeliveryModels`. The create path runs `ValidateAndAIFix`, which only repairs fields that are *present but invalid* within a fixed allowlist — it does **not** invent missing ones, and `Solutions`, `TargetCloseDate` and `ExpectedCustomerSpend` aren't auto-fixable at all: Fours never guesses a monthly revenue. The authoritative list is the [AWS CreateOpportunity reference](https://docs.aws.amazon.com/partner-central/latest/APIReference/API_CreateOpportunity.html).

### The TargetCloseDate policy

A past close date is not simply rejected. Fours decides per case, so a recently-slipped opportunity still goes through while a stale one is surfaced instead of being quietly laundered into a future date:

| `LifeCycle.TargetCloseDate` | What happens |
|---|---|
| Missing or unparseable | **Rejected** |
| More than 30 days in the past | **Rejected** as stale |
| In the past, but within the last 30 days | Auto-pushed forward to today + 30 days |
| Today or in the future | Kept, reformatted to `YYYY-MM-DD` |

### Where Solution IDs come from

AWS has no solution-lookup endpoint (unlike Azure). Your Solution IDs are synced from ACE onto the integration itself — read them from `GET /org/{orgId}/integration/AWS/ACE`:

- `info.awsAceIntegration.offeringsV2[]` — each entry is `{ id, type, title }`; `id` is what goes in `Solutions`
- `info.awsAceIntegration.offerings[]` — the older flat array of id strings

The same response carries `info.awsAceIntegration.catalog`, which tells you whether you are pointed at `AWS` (production) or `Sandbox`.

### Auto-filled / normalized by Fours

- `Catalog` — set from your connected AWS integration (`AWS` or `Sandbox`). Don't send it.
- `Project.ExpectedCustomerSpend[0]` — the first entry's `Frequency`→`Monthly`, `CurrencyCode`→`USD`, `TargetCompany`→`AWS` are always overwritten (AWS only accepts these for this object). You supply `Amount`.
- `Marketing.Source` defaults to `"None"`; `Project.SalesActivities` defaults to `["Initialized discussions with customer"]` when empty or when every value is invalid.
- `NationalSecurity` defaults to `"Yes"` when `Industry` is `"Government"` **and** you left it blank or sent something other than `"Yes"`/`"No"`. An explicit `"No"` is preserved.
- `LifeCycle.Stage` is cleared if it isn't a valid stage. `Project.CompetitorName`, `Customer.Account.Duns` (9 digits) and `Customer.Account.AwsAccountId` (12 digits) are cleared if invalid.
- `Project.AdditionalComments` and `LifeCycle.NextSteps` are truncated to 255 characters.
- `OpportunityTeam` members with no usable email are **dropped**. Customer `Contacts` are not dropped — an unusable email or phone is just blanked.
- `OpportunityTeam[0]` defaults to `BusinessTitle: "PartnerAccountManager"` and `OpportunityTeam[1]` to `"OpportunityOwner"`. The `PartnerAccountManager` also gets a default phone number if theirs is missing or unusable.
- `SoftwareRevenue` — AWS accepts its five fields only when your AWS ACE integration's partner type is `ISV Accelerator Program Partner` or `SaaS Revenue Recognition Program`. For any other partner type, or none, Fours drops the whole block on create, and an update doesn't apply it. For an eligible partner type, Fours keeps what you send and fills in anything missing or invalid:

  | Field | When missing or invalid |
  | --- | --- |
  | `SoftwareRevenue.Value.Amount` | `Project.ExpectedCustomerSpend[0].Amount` when that is a positive number, otherwise `"0"`. An amount you send must be a positive number; it is rounded to two decimal places. |
  | `SoftwareRevenue.Value.CurrencyCode` | `USD`. A code outside AWS's currency list is dropped first. |
  | `SoftwareRevenue.DeliveryModel` | `Contract`. A value outside AWS's list is dropped first. |
  | `SoftwareRevenue.EffectiveDate` | `LifeCycle.TargetCloseDate`. Dates are sent as `YYYY-MM-DD`, and one that can't be read is dropped. |
  | `SoftwareRevenue.ExpirationDate` | `LifeCycle.TargetCloseDate` plus one year. An end date that isn't after the start date is replaced the same way — or set to a year after the start, when that default still wouldn't follow it. |

### AWS field options

`PrimaryNeedsFromAws` — at least one. Any `Co-Sell - *` value asks AWS for help; `Do Not Need Support from AWS Sales Rep` files the opportunity without it.

`Co-Sell - Architectural Validation` · `Co-Sell - Business Presentation` · `Co-Sell - Competitive Information` · `Co-Sell - Pricing Assistance` · `Co-Sell - Technical Consultation` · `Co-Sell - Total Cost of Ownership Evaluation` · `Co-Sell - Deal Support` · `Co-Sell - Support for Public Tender / RFx` · `Do Not Need Support from AWS Sales Rep`

`OpportunityType` — `Net New Business` · `Flat Renewal` · `Expansion`

`Project.DeliveryModels` — `SaaS or PaaS` · `BYOL or AMI` · `Managed Services` · `Professional Services` · `Resell` · `Other`

`Project.SalesActivities` — `Initialized discussions with customer` · `Customer has shown interest in solution` · `Conducted POC / Demo` · `In evaluation / planning stage` · `Agreed on solution to Business Problem` · `Completed Action Plan` · `Finalized Deployment Need` · `SOW Signed`

`LifeCycle.Stage` — `Prospect` · `Qualified` · `Technical Validation` · `Business Validation` · `Committed` · `Launched` · `Closed Lost`

`Customer.Account.Industry` — `Aerospace` · `Agriculture` · `Automotive` · `Computers and Electronics` · `Consumer Goods` · `Education` · `Financial Services` · `Gaming` · `Government` · `Healthcare` · `Hospitality` · `Life Sciences` · `Manufacturing` · `Marketing and Advertising` · `Media and Entertainment` · `Mining` · `Non-Profit Organization` · `Energy - Oil and Gas` · `Other` · `Energy - Power and Utilities` · `Professional Services` · `Real Estate and Construction` · `Retail` · `Software and Internet` · `Telecommunications` · `Transportation and Logistics` · `Travel` · `Wholesale and Distribution`

`Project.CustomerUseCase` — `AI Machine Learning and Analytics` · `Archiving` · `Big Data: Data Warehouse / Data Integration / ETL / Data Lake / BI` · `Blockchain` · `Business Applications: Mainframe Modernization` · `Business Applications & Contact Center` · `Business Applications & SAP Production` · `Centralized Operations Management` · `Cloud Management Tools` · `Cloud Management Tools & DevOps with Continuous Integration & Continuous Delivery (CICD)` · `Configuration, Compliance & Auditing` · `Connected Services` · `Containers & Serverless` · `Content Delivery & Edge Services` · `Database` · `Edge Computing / End User Computing` · `Energy` · `Enterprise Governance & Controls` · `Enterprise Resource Planning` · `Financial Services` · `Healthcare and Life Sciences` · `High Performance Computing` · `Hybrid Application Platform` · `Industrial Software` · `IOT` · `Manufacturing, Supply Chain and Operations` · `Media & High performance computing (HPC)` · `Migration / Database Migration` · `Monitoring, logging and performance` · `Monitoring & Observability` · `Networking` · `Outpost` · `SAP` · `Security & Compliance` · `Storage & Backup` · `Training` · `VMC` · `VMWare` · `Web development & DevOps`

For `CountryCode` (ISO 3166-1 alpha-2) and the `AwsProducts` list, see the [AWS CreateOpportunity API reference](https://docs.aws.amazon.com/partner-central/latest/APIReference/API_CreateOpportunity.html).

### Example payload (AWS)

This mirrors the field set the Fours Console submits (the *Share with AWS* form), so it includes the Fours-required fields, the fields AWS needs to accept the referral, and the common optional fields. It is **not** the bare validator minimum (see the two lists above). Comments mark `// required`, `// optional`, and which fields Fours fills. Treat the [AWS reference](https://docs.aws.amazon.com/partner-central/latest/APIReference/API_CreateOpportunity.html) as the source of truth for what AWS will accept.

```jsonc
{
  "partner": "AWS",
  "salesforceOpportunityId": "0065g00000XXXXXAAA",   // optional: link to the CRM opportunity
  "salesforceAccountId": "0015g00000YYYYYAAA",        // optional: auto-derived from the opportunity if omitted
  "info": {
    "aceOpportunityV2": {
      "Origin": "Partner Referral",          // required (only valid value for Catalog: AWS)
      "OpportunityType": "Net New Business",  // required: Net New Business | Flat Renewal | Expansion
      "NationalSecurity": "No",               // optional ("Yes"/"No"); forced "Yes" when Industry = "Government"

      "Customer": {
        "Account": {
          "CompanyName": "Acme Robotics Inc",            // required
          "Industry": "Software and Internet",            // required (valid Industry value)
          "WebsiteUrl": "https://www.acmerobotics.com",   // required (waived only if NationalSecurity = "Yes")
          "Duns": "123456789",                            // optional (9 digits)
          "AwsAccountId": "123456789012",                 // optional (12 digits)
          "Address": {
            "CountryCode": "US",                           // required (ISO 3166-1 alpha-2)
            "StateOrRegion": "California",                 // required when CountryCode = "US"
            "City": "San Francisco",                       // optional
            "PostalCode": "94105",                         // required
            "StreetAddress": "123 Market St"               // optional
          }
        },
        "Contacts": [                                       // customer contact(s)
          { "FirstName": "Dana", "LastName": "Cruz", "Email": "dana.cruz@acmerobotics.com", "Phone": "+14155550123", "BusinessTitle": "VP Engineering" }
        ]
      },

      "PrimaryNeedsFromAws": ["Co-Sell - Deal Support"],  // required, >= 1

      "Project": {
        "Title": "Acme migration to AWS",                  // required
        "CustomerBusinessProblem": "Acme needs to migrate its on-prem fleet platform to AWS to cut latency and scale globally.", // required, 20-2000 chars
        "CustomerUseCase": "Migration / Database Migration", // required (valid use-case value)
        "DeliveryModels": ["SaaS or PaaS"],                // required: SaaS or PaaS | BYOL or AMI | Managed Services | Professional Services | Resell | Other
        "SalesActivities": ["Initialized discussions with customer"], // Fours defaults this if omitted
        "ExpectedCustomerSpend": [
          { "Amount": "5000" }                             // required; Fours sets CurrencyCode=USD, Frequency=Monthly, TargetCompany=AWS
        ],
        "CompetitorName": "",                              // optional
        "ApnPrograms": [],                                 // optional
        "AdditionalComments": "Joint GTM with the AWS account team." // optional (<= 255 chars)
      },

      "LifeCycle": {
        "TargetCloseDate": "2027-06-30",                   // required; YYYY-MM-DD — see "The TargetCloseDate policy" above
        "NextSteps": "Schedule joint architecture review"  // optional (<= 255 chars)
      },

      "RelatedEntityIdentifiers": {
        "Solutions": ["S-0123456"],                        // required, >= 1 — from GET /org/{orgId}/integration/AWS/ACE
        "AwsProducts": ["AmazonBedrock"]                   // optional (values from the AWS products list)
      },

      "OpportunityTeam": [                                  // your team (partner side); members without an email are dropped
        { "FirstName": "Sam", "LastName": "Patel", "Email": "sam.patel@yourcompany.com", "Phone": "+14155550100", "BusinessTitle": "PartnerAccountManager" },
        { "FirstName": "Jordan", "LastName": "Lee", "Email": "jordan.lee@yourcompany.com", "Phone": "+14155550101", "BusinessTitle": "OpportunityOwner" }
      ],

      "Marketing": { "Source": "None" }                    // Fours fills "None"; if set to a real source, also provide CampaignName / Channels / UseCases / AwsFundingUsed
    }
  }
}
```

> `Catalog` and `SoftwareRevenue` are intentionally omitted — Fours sets `Catalog` from your AWS integration, and AWS accepts `SoftwareRevenue` only from `ISV Accelerator Program Partner` and `SaaS Revenue Recognition Program` partners (see [Auto-filled / normalized by Fours](#auto-filled--normalized-by-fours) for how Fours fills it in). `PartnerOpportunityIdentifier` is set to the referral ID for you.

---

## Azure (Microsoft)

Send the opportunity under `info.createMicrosoftReferralRequest`. Field keys use **camelCase**.

### Required by Fours

| Field (under `info.createMicrosoftReferralRequest`) | Rule |
|---|---|
| `name` | Deal name, non-empty |
| `details.closingDateTime` | UTC datetime (`YYYY-MM-DDTHH:MM:SSZ`), today or future |
| `details.dealValue` | Number > 0 |
| `details.currency` | ISO 4217 code (defaults to `USD` if blank) |
| `customerProfile.address.country` | ISO 3166-1 alpha-2 (defaults to `US` if blank) |
| `team[]` | At least one member, each with `email`, `firstName`, `lastName`, `phoneNumber` |
| `details.requirements.solutions[].id` | At least one solution (for `Private` / `IPCosell` deals) |
| `details.requirements.additionalRequirements.attributes[]` | One `SolutionArea` and one matching `Conversation` attribute; plus `CustomerMarketplaceIntent` for `Private`/`IPCosell`. See [Field options](#field-options) |

### Co-sell vs. independent

A referral is treated as a **co-sell** (Microsoft is asked to help) when it carries an invitation with `organizationId: "msft"` and an `assistanceRequestCode` other than `NoHelpRequired`. For co-sell referrals you must **also** provide a customer contact: at least one `customerProfile.team[]` entry with `email`, `firstName`, `lastName`, `phoneNumber` (and its email domain must differ from your partner team's domains).

Fours auto-sets `type`, `status`, `substatus` and `dealType`, and auto-fixes missing phone numbers.

### Field options

#### `dealType`

Fours derives this for you — an invitation asking Microsoft for help makes it `IPCosell`, no invitation makes it `Private` — so you only need to send it to choose `ServicesCosell`.

| Value | Label in the console | Requires | Silently removed |
|---|---|---|---|
| `Private` | Private | `solutions[]`, `CustomerMarketplaceIntent` | `PartnerRole` |
| `IPCosell` | IP Co-sell | `solutions[]`, `CustomerMarketplaceIntent` | `PartnerRole` |
| `ServicesCosell` | Service Co-sell | `PartnerRole` (`solutions[]` optional) | `CustomerMarketplaceIntent` |

#### `assistanceRequestCode`

Set on each `invitations[]` entry. Anything other than `NoHelpRequired` turns the referral into a co-sell.

| Value | Meaning |
|---|---|
| `NoHelpRequired` | No help required at this point of time |
| `WorkloadSpecificValueProposition` | Workload-specific value proposition |
| `CustomerTechnicalArchitecture` | Customer technical architecture |
| `ProofOfConceptOrDemo` | Proof of concept / Demo |
| `QuotesOrLicensing` | Quotes / Licensing |
| `PostSalesCustomerSuccess` | Post-sales customer success |
| `GeneralOrOther` | General or other |

#### `SolutionArea` and `Conversation`

Send both as entries in `details.requirements.additionalRequirements.attributes[]`, where `id` is the display string below — not a GUID. Microsoft validates the **pair**, so a valid conversation under the wrong solution area is rejected.

| `SolutionArea` | Valid `Conversation` values |
|---|---|
| `AI Business Solutions` | `AI-ready productivity and security for every employee`<br />`Agentify your business processes`<br />`AI in the flow of human ambition` |
| `Cloud and AI Platforms` | `Modernize with confidence`<br />`Build a unified governed data and AI estate`<br />`Amplify your intelligence`<br />`Ubiquitous Innovation` |
| `Microsoft Services` | `Accelerate AI Transformation with Microsoft Services` |
| `Security` | `AI-ready productivity and security for every employee`<br />`Establish a trusted and secure platform for AI` |
| `Windows and Devices` | `AI Transformation with Surface and Copilot` |

:::note
**`SolutionPlay` was renamed to `Conversation`.** Send `Conversation` on new referrals. `SolutionPlay` is still accepted — on create Fours rewrites it to `Conversation`, and on update it keeps whichever name you sent — but it is the retired name. Values from earlier Microsoft fiscal years are mapped forward automatically on create. See [Co-Sell Fields in Microsoft](/cosell/reference/microsoft-fields/) for the fiscal-year rules and how legacy values are displayed.
:::

#### `CustomerMarketplaceIntent`

Required for `Private` and `IPCosell`. If you omit it or send an unrecognized value, Fours defaults it to `HaveNotDecided` rather than rejecting the referral.

| Value | Label in the console |
|---|---|
| `Yes` | Yes |
| `No` | No |
| `HaveNotDecided` | Have not decided |

#### `PartnerRole`

Required for `ServicesCosell` only, and removed from other deal types.

`Adoption and change management` · `Business strategy` · `Deployment services` · `Managed services` · `Presales envisioning` · `Proof of concept` · `Solution design` · `Transaction`

#### `currency` and `country`

`details.currency` is an ISO 4217 code and `customerProfile.address.country` is an ISO 3166-1 alpha-2 code, both validated against Microsoft's picklists. An unrecognized value is not an error — Fours falls back to `USD` and `US` respectively. For the full lists see the [Microsoft "Create a referral" reference](https://learn.microsoft.com/en-us/partner-center/developer/create-a-referral).

### Example payload (Azure, independent / no Microsoft help)

This mirrors the field set the Fours Console submits. Comments mark `// required` / `// optional` and which fields Fours fills.

```jsonc
{
  "partner": "AZURE",
  "salesforceOpportunityId": "0065g00000XXXXXAAA",   // optional: link to the CRM opportunity
  "salesforceAccountId": "0015g00000YYYYYAAA",        // optional: auto-derived from the opportunity if omitted
  "info": {
    "createMicrosoftReferralRequest": {
      "name": "Acme – Azure migration",                // required (deal name)
      "externalReferenceId": "0065g00000XXXXXAAA",      // optional: your CRM record id
      "team": [                                         // required: >= 1 partner contact, each with email/firstName/lastName/phoneNumber
        { "firstName": "Sam", "lastName": "Patel", "email": "sam.patel@yourcompany.com", "phoneNumber": "+14155550100", "title": "Partner Account Manager" }
      ],
      "customerProfile": {
        "name": "Acme Robotics Inc",                    // optional
        "address": {
          "country": "US",                               // required (ISO 3166-1 alpha-2; Fours defaults "US")
          "city": "San Francisco",                       // optional
          "region": "CA",                                // optional
          "postalCode": "94105"                          // optional
        }
      },
      "details": {
        "closingDateTime": "2027-06-30T23:59:59Z",       // required (UTC; today or future)
        "currency": "USD",                               // required (ISO 4217; Fours defaults "USD")
        "dealValue": 50000,                              // required (> 0)
        "notes": "Joint migration opportunity.",         // optional
        "requirements": {
          "solutions": [{ "id": "your-solution-id" }],   // required (>= 1) for Private / IPCosell deals
          "additionalRequirements": {
            "attributes": [                               // required — see "Field options" above
              { "type": "SolutionArea", "id": "Cloud and AI Platforms" },
              { "type": "Conversation", "id": "Modernize with confidence" },
              { "type": "CustomerMarketplaceIntent", "id": "HaveNotDecided" }
            ]
          }
        }
      }
    }
  }
}
```

> Fours sets `type`, `status`, `substatus`, and `dealType` for you and auto-fixes missing phone numbers — don't send those.

To request Microsoft co-sell help, add an `invitations` entry and a customer contact (the customer contact is **required** once Microsoft help is requested):

```jsonc
"invitations": [
  { "organizationId": "msft", "organizationName": "Microsoft", "assistanceRequestCode": "ProofOfConceptOrDemo" }
],
"customerProfile": {
  "address": { "country": "US" },
  "team": [                                              // required for co-sell: >= 1 customer contact
    { "firstName": "Dana", "lastName": "Cruz", "email": "dana.cruz@acmerobotics.com", "phoneNumber": "+14155550200" }
  ]
}
```

---

## GCP (Google Cloud)

Send the opportunity under `info.gcpOpportunityV2`. Field keys use **camelCase**.

GCP has **two opportunity types**, selected by the top-level `opportunityType` field:

- **Regular** — `opportunityType` is `REGULAR` (or omitted / `TWO_TIER`). A standard resell/services opportunity.
- **ISV Solution Connect** — `opportunityType` is `ISV_SOLUTION_CONNECT`. An ISV solution deal, with its own `isvSolutionConnectInfo` block.

The two types share a customer block but differ in which qualification and product fields are required.

### Required for both types

| Field (under `info.gcpOpportunityV2`) | Rule |
|---|---|
| `opportunityInfo.partnerEntity` | Format `partners/{id}` |
| `customerInfo.customerDetails.organizationName` | Non-empty |
| `customerInfo.customerDetails.domain` | Valid domain |
| `customerInfo.customerDetails.address.regionCode` | ISO 3166-1 alpha-2 |
| `customerInfo.customerDetails.address.postalCode` | Required for most countries (US: `12345` or `12345-6789`) |
| `customerInfo.customerDetails.industry` | From the GCP industry list |
| `customerInfo.customerDetails.employeeCount` | Integer > 0 |
| `customerInfo.contact.givenName`, `.familyName`, `.email` | All required |
| `customerInfo.region` | One of `ANZ`, `CEE`, `DACH`, `FRANCE`, `INDIA`, `JAPAN`, `LATAM_BRAZIL`, `LATAM_SPANISH_SPEAKING`, `NE`, `NORTH_AMERICA`, `OTHER_ASIA_PACIFIC`, `SEEMEA`, `UK_AND_I` |
| `qualificationInfo.contractLengthMonths` | Integer 1–96 |
| `qualificationInfo.estimatedCloseDate` | `{ year, month, day }`, future date |
| `qualificationInfo.dealSize.currencyCode` | `USD` for GCP/Google Maps product categories |
| `qualificationInfo.dealSize.units` | Positive integer **as a string**, e.g. `"50000"` |
| `qualificationInfo.legalLanguageAccepted` | Must be `true` (GCP requirement) |

### Required for Regular only

| Field | Rule |
|---|---|
| `opportunityInfo.productCategory[0]` | From the GCP product-category list (e.g. `GCP_COMPUTE`, `GCP_AI`, `GOOGLE_MAPS`) |
| `qualificationInfo.operationType` | `NEW`, `RENEWAL`, `ADD_ON`, `EXPANSION` |
| `qualificationInfo.quantity` | Integer ≥ 1 |
| `qualificationInfo.budget` | `EXACT_BUDGET_GIVEN`, `BUDGET_EXISTS_NOT_SPECIFIED`, `CONFIRMED_NO_BUDGET`, `REFUSE_TO_DISCLOSE` |
| `qualificationInfo.authority` | `CLEARLY_IDENTIFIED_PROCESS`, `CONFIRMED_NO_AUTHORITY_DECIDED`, `VAGUE_UNDERSTANDING`, `DONT_KNOW` |
| `qualificationInfo.need` | `CLEAR_NEED`, `COMPELLING_NEED`, `NO_NEED` |
| `qualificationInfo.timeline` | `SPECIFIC_DECISION_DATES_GIVEN`, `VAGUE_DECISION_DATES_GIVEN`, `CONFIRMED_NO_DECISION_DATE`, `REFUSED_TO_GIVE_DATE` |
| `qualificationInfo.decisionPhase` | `FINAL_DECISION`, `TRIAL_OR_PILOT`, `INFORMATION_GATHERING` |

### Required for ISV Solution Connect only

| Field | Rule |
|---|---|
| `opportunityInfo.productFamily[0]` | `GOOGLE_CLOUD_PLATFORM` or `GOOGLE_WORKSPACE` |
| `opportunityInfo.description` | Non-empty |
| `isvSolutionConnectInfo.contractVehicle` | `CLOUD_MARKETPLACE`, `PARTNER_CONTRACT`, `UNKNOWN` |
| `isvSolutionConnectInfo.deliveryModel` | `DATA_TO_CUSTOMER_BIGQUERY`, `VM_ON_CUSTOMER_TENANCY`, `SAAS_ON_GOOGLE_CLOUD`, `OTHER_DELIVERY_MODEL` |
| `isvSolutionConnectInfo.supportLevel` | `TECH`, `TRANSACTION`, `UPSELL_RENEW_CROSS_SELL`, `ALIGN_SALES_ACCOUNT`, `ALREADY_CONTACT`, `OTHER_SUPPORT_LEVEL` |

For the full `industry`, `productCategory`, currency, and region lists, see the [Google Cloud opportunities API reference](https://docs.cloud.google.com/channel/docs/reference/rest/v1alpha1/opportunities).

### Example payload (GCP — Regular)

```jsonc
{
  "partner": "GCP",
  "salesforceOpportunityId": "0065g00000XXXXXAAA",   // optional: link to the CRM opportunity
  "salesforceAccountId": "0015g00000YYYYYAAA",        // optional: auto-derived from the opportunity if omitted
  "info": {
    "gcpOpportunityV2": {
      "opportunityType": "REGULAR",                    // REGULAR | TWO_TIER | ISV_SOLUTION_CONNECT
      "opportunityInfo": {
        "partnerEntity": "partners/123456",            // required (partners/{id})
        "productCategory": ["GCP_COMPUTE"],            // required (>= 1; e.g. GCP_AI, GCP_ANALYTICS, GCP_DATABASES)
        "displayName": "Acme Corp – GCP Compute",      // optional (auto-generated if omitted)
        "confidential": false                          // optional
      },
      "customerInfo": {
        "customerDetails": {
          "organizationName": "Acme Corporation",      // required
          "domain": "acme.com",                        // required
          "industry": "SOFTWARE",                      // required (GCP industry value)
          "employeeCount": 500,                        // required (> 0)
          "address": { "regionCode": "US", "postalCode": "94043" }  // required (regionCode; postalCode for most countries)
        },
        "contact": {
          "givenName": "John", "familyName": "Doe", "email": "john.doe@acme.com",  // required
          "phone": { "e164Number": "+14155550123" }    // optional
        },
        "region": "NORTH_AMERICA"                       // required (GCP sales region)
      },
      "qualificationInfo": {
        "operationType": "NEW",                         // required: NEW | RENEWAL | ADD_ON | EXPANSION
        "quantity": 1,                                  // required (>= 1)
        "contractLengthMonths": 12,                     // required (1–96)
        "dealSize": { "currencyCode": "USD", "units": "50000" },        // required (USD for GCP; units as a string)
        "estimatedCloseDate": { "year": 2026, "month": 9, "day": 30 },  // required (future)
        "budget": "EXACT_BUDGET_GIVEN",                 // required
        "authority": "CLEARLY_IDENTIFIED_PROCESS",      // required
        "need": "CLEAR_NEED",                           // required
        "timeline": "SPECIFIC_DECISION_DATES_GIVEN",    // required
        "decisionPhase": "FINAL_DECISION",              // required
        "legalLanguageAccepted": true,                  // required (must be true)
        "publicSector": false,                          // optional
        "msspDeal": false                               // optional
      }
    }
  }
}
```

### Example payload (GCP — ISV Solution Connect)

```jsonc
{
  "partner": "GCP",
  "salesforceOpportunityId": "0065g00000XXXXXAAA",   // optional: link to the CRM opportunity
  "salesforceAccountId": "0015g00000YYYYYAAA",        // optional: auto-derived from the opportunity if omitted
  "info": {
    "gcpOpportunityV2": {
      "opportunityType": "ISV_SOLUTION_CONNECT",       // required for ISV
      "opportunityInfo": {
        "partnerEntity": "partners/123456",            // required (partners/{id})
        "productFamily": ["GOOGLE_CLOUD_PLATFORM"],    // required for ISV: GOOGLE_CLOUD_PLATFORM | GOOGLE_WORKSPACE
        "description": "Deploying our real-time analytics SaaS on GCP for the customer."  // required for ISV
      },
      "customerInfo": {
        "customerDetails": {
          "organizationName": "TechCorp Inc",          // required
          "domain": "techcorp.io",                     // required
          "industry": "FINANCIAL_SERVICES",            // required
          "employeeCount": 2000,                       // required
          "address": { "regionCode": "GB", "postalCode": "SW1A 1AA" }  // required
        },
        "contact": {
          "givenName": "Jane", "familyName": "Smith", "email": "jane.smith@techcorp.io",  // required
          "phone": { "e164Number": "+442071234567" }   // optional
        },
        "region": "UK_AND_I"                            // required (GCP sales region)
      },
      "qualificationInfo": {
        "contractLengthMonths": 24,                     // required (1–96)
        "dealSize": { "currencyCode": "USD", "units": "250000" },       // required
        "estimatedCloseDate": { "year": 2026, "month": 6, "day": 30 },  // required (future)
        "legalLanguageAccepted": true                   // required (must be true)
      },
      "isvSolutionConnectInfo": {
        "isvSolutionConnectDeal": true,                 // required for ISV
        "contractVehicle": "CLOUD_MARKETPLACE",         // required for ISV
        "deliveryModel": "SAAS_ON_GOOGLE_CLOUD",        // required for ISV
        "supportLevel": "TECH",                         // required for ISV
        "customerContactRequested": false               // optional
      }
    }
  }
}
```

> ISV Solution Connect does **not** require the qualification fields (`operationType`, `budget`, `authority`, `need`, `timeline`, `decisionPhase`, `quantity`, `productCategory`) that a Regular opportunity does — Fours' validator only enforces those for Regular. Google's API is the final authority; see the reference linked above.

---

## Response & next steps

A successful create returns `OperationExecutionDetails`, including a `workflowID` of the form `org/{orgId}/cosell/referral/{referralId}/create` — the `{referralId}` segment is the new referral's ID.

- The referral lands in `PENDING_CREATE` while the submission workflow runs, then moves to the partner's accepted state — or `CREATE_FAILED` if the partner API rejects it. To resubmit, `GET` the referral, correct it, and `POST` it back to `.../cosell/referral` with the same `id` — Fours resubmits that referral in place. A body without the `id` creates a new referral instead.
- Do **not** call sync for the initial submit — submission is automatic.
- To edit a referral later, use `PATCH /org/{orgId}/cosell/referral/{referralId}`. The outbound sync to the partner starts automatically — do not call sync afterwards. Two cases send nothing; see [Updating a referral](#updating-a-referral).

### Referral IDs and retries

`id` is optional and yours to choose: any string of up to 36 characters, unique in your organization. When you omit it, Fours generates a UUID. Always send one, so a retry can be matched to the first attempt.

The partner submission runs after create returns, so a `500` never means the partner already has the opportunity. But calling create again with the same `id` overwrites the stored referral and submits it again, and AWS may reject the repeat as a duplicate record, leaving the referral `CREATE_FAILED`. Before you retry, `GET` the referral by your `id`:

| What you find | What to do |
|---|---|
| Not found | Retry with the same body. |
| `PENDING_CREATE` | The submission is in progress: wait and poll. The exception is a `500` that said the create workflow failed to start — then retry with the same body. |
| `CREATE_FAILED` | Resubmit it, as described above. |
| Any other status | The create succeeded. Do not call create again. |

To resubmit an Azure referral, send the corrected `info.createMicrosoftReferralRequest` — and only while `info.microsoftReferralSet.self.id` is still empty. A non-empty one means Partner Center already holds the referral.

## Updating a referral

`PATCH /org/{orgId}/cosell/referral/{referralId}` edits a referral. By default the partner-specific update validator runs and an outbound sync to the partner is started.

Two kinds of update are saved but not sent to the partner:

- **The referral approval process is on.** For AWS, Azure, or GCP, an update to a referral that is not yet approved or submitted is saved as `DRAFT`, and nothing is sent until it is approved in the Fours Console.
- **An AWS referral at `Launched` or `Closed Lost`.** A content update is accepted and saved in Fours, but the outbound sync skips a closed AWS opportunity, so the change never reaches AWS.

- **Query param:** `isLinkageUpdate` (boolean, optional, default `false`). When `true`, only the CRM-linkage fields — `salesforceOpportunityId`, `salesforceAccountId`, `hubspotDealId`, `dynamics365OpportunityId` — are persisted; the partner-opportunity content in the body is ignored, and the update validator and outbound sync are **skipped**. Sending all four empty unlinks the referral.
- Use it for a pure CRM **link or unlink** (e.g. attaching a referral to a Salesforce opportunity/account), so the linkage is saved even when the referral's partner-opportunity state would otherwise fail validation — for example an AWS referral already at the *Committed* stage. Honored for **AWS** and **GCP**; for **Azure** and **Fours** the flag is ignored and the normal update path is used.
- **An AWS referral in `CREATE_FAILED` can't be edited here.** AWS never accepted it, so there is nothing to update: resubmit it instead, by sending it back to `POST .../cosell/referral` with the same `id` (see [Response & next steps](#response--next-steps)). The `PATCH` is refused with `400`, and the referral is left unchanged:

  ```jsonc
  // excerpt of the 400 response
  "info": [
    { "field": "status", "message": "referral <referralId> failed to create (status CREATE_FAILED) and cannot be edited via update; fetch it with GetCosellReferral and resubmit it by calling CreateCosellReferral again with the same id" }
  ]
  ```

  A linkage-only update (`isLinkageUpdate=true`) is not affected.

- **An AWS referral that AWS is still reviewing can't be edited at all** (status `IN_REVIEW`, `lifeCycle.reviewStatus` `Submitted` or `In review`). The update is refused before anything is sent to ACE or saved: the call returns `500` with a message ending `cannot update opportunity, status: <reviewStatus>`, and the referral is left exactly as it was. Wait for the review to reach *Approved* or *Action Required*, then retry.

### Confirming the partner took the change

A `200` means the change is saved in Fours, not that the partner accepted it. The response's `workflowID` identifies the push to the partner: poll [Get operation](/api/get-operation-v2/) with it (URL-encoded) until its status leaves `Running`. `Completed` means the partner took the change; `Failed` means it refused, and the partner's reason is added to the referral's `errorMessages`.

### Partial updates (`merge=true`, AWS only)

By default a `PATCH` body **replaces** the referral's stored content, so every field has to be sent. Pass `merge=true` — as a query parameter or in the body — to patch only the fields you send instead. It is supported for **AWS only**, and the body must always carry `"partner": "AWS"`.

- **What merge does with each field.** A field you omit keeps its stored value, a field you send replaces it, and an explicit `null` clears it. The exceptions are `info.aceOpportunityV2.lifeCycle.reviewStatus` and `lifeCycle.stage`: they cannot be cleared, so sending either as `null` keeps its stored value rather than emptying it.
- **Objects vs. lists.** Nested objects merge key by key; lists (such as contacts or solutions) are replaced whole, not merged — send the complete list when you change one entry.
- **What is preserved.** Merging covers the referral's top-level fields and `info.aceOpportunityV2`. Other `info` sub-documents — for example `aceEngagementInvitation` — are not preserved by an update.
- **When it is rejected with `400`.** For a non-AWS partner, when combined with `isLinkageUpdate=true`, and for an engagement-invitation accept/reject — send the full referral body for those.

The outbound sync to the partner still starts automatically after a merge update; do not call sync afterwards.

:::tip
AI agents and MCP clients should always send `merge=true`, so an omitted field is never mistaken for one you meant to clear.
:::

### Azure updates replace the referral

An Azure update always replaces the referral, and `merge=true` is rejected with `400`. Fetch the referral, edit `info.microsoftReferralSet.self`, and send the whole referral back with `"partner": "AZURE"`. Start from the body the read returned with your edit applied; do not assemble a minimal one.

- **Kept whatever you send:** the read-only fields (`id`, `engagementId`, `type`, `eTag`, `organizationId`, `mpnId`, `expirationDateTime` and the timestamps), and `registrations`.
- **Can't be removed** (`400`): `name`, `details`, `details.requirements`, `team` and `target`.
- **Must be sent back:** `status`, `substatus` and `salesStage`, or the update is rejected — a stored `salesStage` with none sent back fails as `Invalid sales stage ''`. Send `direction` back exactly as the read returned it (`Outgoing` or `Incoming`), since the referral's status is derived from it.
- **Dropped when omitted:** everything else, for example `customerProfile`, `consent`, `details.notes`, `externalReferenceId` (your CRM cross-reference in Partner Center), `inviteContext` (the Microsoft assistance request on a co-sell), `qualification`, `campaignId`, `statusReason`, and the top-level CRM links (`salesforceOpportunityId`, `salesforceAccountId`, `salesforceReferralId`, `hubspotDealId`, `dynamics365OpportunityId`). Omitting `details.requirements.additionalRequirements.attributes` clears the solution area, conversation and marketplace intent without an error.

The note field is `details.notes`; there is no `self.notes`, and a value sent there is silently discarded. Microsoft's copy of a co-sell (`info.microsoftReferralSet.connected`) is never changed by an update. The [update reference](/api/update-cosell-referral/#example-request) has a full Azure example.

## Call it from Salesforce (Apex)

If you use the **Fours app** managed package for Salesforce, you can create a referral from Apex without writing your own HTTP callout. Call the packaged method with the same payload shape described above (note the `Suger.` namespace prefix):

```java
Map<String, Object> referral = new Map<String, Object>{
  'partner' => 'AWS',
  'salesforceOpportunityId' => opp.Id,         // optional: link to the CRM opportunity
  'salesforceAccountId' => opp.AccountId,       // optional: auto-derived from the opportunity if omitted
  'info' => new Map<String, Object>{
    'aceOpportunityV2' => new Map<String, Object>{
      'Origin'          => 'Partner Referral',   // required
      'OpportunityType' => 'Net New Business',    // required
      'NationalSecurity' => 'No',                 // optional
      'Customer' => new Map<String, Object>{
        'Account' => new Map<String, Object>{
          'CompanyName' => 'Acme Robotics Inc',          // required
          'Industry'    => 'Software and Internet',       // required
          'WebsiteUrl'  => 'https://www.acmerobotics.com',// required
          'Duns'        => '123456789',                   // optional
          'AwsAccountId' => '123456789012',               // optional
          'Address'     => new Map<String, Object>{
            'CountryCode' => 'US',                          // required
            'StateOrRegion' => 'California',                // required for US
            'City' => 'San Francisco',
            'PostalCode' => '94105',                        // required
            'StreetAddress' => '123 Market St'
          }
        },
        'Contacts' => new List<Object>{
          new Map<String, Object>{ 'FirstName' => 'Dana', 'LastName' => 'Cruz', 'Email' => 'dana.cruz@acmerobotics.com', 'Phone' => '+14155550123', 'BusinessTitle' => 'VP Engineering' }
        }
      },
      'PrimaryNeedsFromAws' => new List<String>{ 'Co-Sell - Deal Support' },   // required
      'Project' => new Map<String, Object>{
        'Title' => 'Acme migration to AWS',                // required
        'CustomerBusinessProblem' => 'Acme needs to migrate its on-prem fleet platform to AWS to cut latency and scale globally.',  // required
        'CustomerUseCase' => 'Migration / Database Migration',  // required
        'DeliveryModels'  => new List<String>{ 'SaaS or PaaS' },// required
        'ExpectedCustomerSpend' => new List<Object>{ new Map<String, Object>{ 'Amount' => '5000' } },  // required (Amount)
        'SalesActivities' => new List<String>{ 'Initialized discussions with customer' }, // Fours defaults if omitted
        'AdditionalComments' => 'Joint GTM with the AWS account team.'  // optional
      },
      'LifeCycle' => new Map<String, Object>{
        'TargetCloseDate' => '2027-06-30',                 // required (future, YYYY-MM-DD)
        'NextSteps' => 'Schedule joint architecture review'// optional
      },
      'RelatedEntityIdentifiers' => new Map<String, Object>{
        'Solutions'   => new List<String>{ 'S-0123456' },  // required, >= 1
        'AwsProducts' => new List<String>{ 'AmazonBedrock' }// optional
      },
      'OpportunityTeam' => new List<Object>{                // your team (partner side)
        new Map<String, Object>{ 'FirstName' => 'Sam', 'LastName' => 'Patel', 'Email' => 'sam.patel@yourcompany.com', 'Phone' => '+14155550100', 'BusinessTitle' => 'PartnerAccountManager' },
        new Map<String, Object>{ 'FirstName' => 'Jordan', 'LastName' => 'Lee', 'Email' => 'jordan.lee@yourcompany.com', 'Phone' => '+14155550101', 'BusinessTitle' => 'OpportunityOwner' }
      },
      'Marketing' => new Map<String, Object>{ 'Source' => 'None' }  // Fours fills "None"
    }
  }
};

// forceCreate=false → fail fast on validation errors; true → save even if the partner rejects, to fix & resubmit
Object result = Suger.CosellApi.createCosellReferral(referral, false);
Map<String, Object> operation = (Map<String, Object>) result;
String workflowId = (String) operation.get('workflowID');
```

- Build `info.aceOpportunityV2` / `info.createMicrosoftReferralRequest` / `info.gcpOpportunityV2` as nested `Map<String, Object>` exactly as the JSON examples above show — the envelope is identical to the REST body.
- The method **makes an HTTP callout**, so it must run in a callout-allowed context: **not after DML in the same transaction**, and from a record-triggered Flow or trigger it must run **asynchronously** (Queueable or `@future(callout=true)`).
- The caller needs co-sell **write** permission for the partner; the Fours endpoint and authentication are handled by the package.
- To edit a referral, call `Suger.CosellApi.updateCosellReferral(referralId, referralData)`. For a pure CRM **link/unlink** (AWS/GCP), call `Suger.CosellApi.updateCosellReferralLinkage(referralId, referralData)` instead — it sends `isLinkageUpdate=true` so the linkage saves even when the referral's partner-opportunity state would fail validation (see [Updating a referral](#updating-a-referral)).
- `updateCosellReferral` calls the same `PATCH` endpoint, so it is refused for an AWS referral in `CREATE_FAILED`. To resubmit one, call `Suger.CosellApi.createCosellReferral` with the referral including its `id`.

### From a Flow (no Apex)

You don't have to write Apex to submit a referral from a Flow. The package ships two invocable actions you can drop straight into Flow Builder:

- **Co-Sell (Fire and Forget)** — submits and returns immediately (no wait, no Salesforce write-back).
- **Co-Sell** — submits, waits for the create to finish, then backfills a Salesforce `Referral__c` record.

Each takes three inputs:

| Input | Required | Notes |
|---|---|---|
| **Partner** | Yes | `AWS`, `AZURE`, or `GCP`. |
| **Opportunity ID** | No | Builds the payload from a CRM opportunity via field mapping (the original behavior). |
| **Referral Payload (JSON)** | No | A hand-built payload — the **same envelope shown above**, as a JSON string. When set, the field-mapping preview is skipped and this payload is submitted as-is. |

Provide **one** of *Opportunity ID* or *Referral Payload (JSON)*. If both are set, the payload wins and the Opportunity ID is used only to link the referral to that CRM record.

Because creating a referral is an HTTP callout, place the action on a record-triggered Flow's **Run Asynchronously** path (a synchronous after-save path can't make the callout). Screen and autolaunched Flows can call it directly as long as no DML ran earlier in the transaction.

:::note
If you don't see the **Referral Payload (JSON)** input in Flow Builder, upgrade the Fours app package. It's available in recent versions. `createCosellReferral` itself is `@AuraEnabled` (not `@InvocableMethod`), so it isn't selectable in Flow directly; these two actions are the Flow entry points, or call it from your own Apex as shown above.
:::

## Listing referrals

`GET /org/{orgId}/cosell/referral` lists both inbound and outbound referrals, with pagination,
sorting, and a filter expression.

### Filter syntax

Pass the filter in `q`. The default query syntax is `lisp`, so an expression looks like this:

```
q=(and (= partner "AWS") (= status "IN_REVIEW"))
```

:::warning
**The equality operator is `=`, not `eq`.** Sending `eq` is rejected with
`400 invalid operator`. A field name the API does not recognise is rejected with
`field <name> not found` — note that this one surfaces as a `500`, not a `400`, so do not rely on the
status code alone to tell a bad operator from a bad field name.
:::

The operators below cover the common cases. The filter language accepts more than these — negated forms
such as `not_like` and `not_between`, array-membership operators, and dash-spelled aliases like `not-in`
— so treat this as the working set rather than the complete grammar:

| Category | Operators |
| --- | --- |
| Comparison | `=` · `!=` · `>` · `>=` · `<` · `<=` |
| Set membership | `in` · `not_in` |
| Text matching | `like` · `ilike` · `contains` · `starts_with` · `ends_with` |
| Null checks | `is_null` · `is_not_null` |
| Range | `between` |
| Combining | `and` · `or` · `not` |

**`in` and `not_in` take their values as separate arguments, with no brackets:**

```
q=(in status "ACTIVE" "IN_REVIEW" "ACTION_REQUIRED")
```

Brackets are not list syntax here. `(in status ["ACTIVE" "IN_REVIEW"])` is read as the three
values `[ACTIVE`, `IN_REVIEW`, and `]`, so the first value silently never matches and no error tells
you so.

Legal values for `status` are the Fours unified statuses, in `UPPER_SNAKE_CASE`: `DRAFT`,
`PENDING_CREATE`, `CREATE_FAILED`, `UPDATE_FAILED`, `IN_REVIEW`, `PENDING_ACCEPTANCE`,
`ACTION_REQUIRED`, `ACTIVE`, `EXPIRED`, `CLOSED_WON`, `CLOSED_LOST`, `CLOSED_ERROR`, and
`REJECTED`.

### Rows the list never returns

Two kinds of row are filtered out server-side, so you cannot retrieve them by asking for them:

| Excluded | Why |
| --- | --- |
| Any referral whose status is `DELETED` | Deleted referrals are hidden from every list. |
| `partner=SUGER` **and** `status=DRAFT` | A Fours partner-to-partner draft is an inbound deal registration still awaiting your review, not a referral yet. It is excluded from this endpoint, from the dashboard, and from the commission ledger. |

The `SUGER` + `DRAFT` exclusion is scoped to that exact pair. **Azure** has its own legitimate
`DRAFT` flow, and Azure drafts are returned normally.

## Full API reference

For the complete request/response schema and to try the endpoint, see [Create Co-Sell Referral](/api/create-cosell-referral/) (`POST /org/{orgId}/cosell/referral`) in the Fours API reference, or run the whole flow from the [Postman collection](#try-it-in-postman). The authoritative field definitions for each cloud are the partner references linked above:

- [AWS — CreateOpportunity](https://docs.aws.amazon.com/partner-central/latest/APIReference/API_CreateOpportunity.html)
- [Microsoft — Create a referral](https://learn.microsoft.com/en-us/partner-center/developer/create-a-referral)
- [Google Cloud — opportunities](https://docs.cloud.google.com/channel/docs/reference/rest/v1alpha1/opportunities)
