Create a Private Offer via API
Overview
Create an Oracle Cloud Marketplace private offer programmatically with the Fours
CreateOffer endpoint. Fours creates the offer in OCI, attaches the
contract document, and sends it to the buyer — the same flow the console runs, driven from your own
system.
The call is asynchronous. Fours validates the request, persists a DRAFT offer, returns it
immediately, and then works with OCI in the background. The offer’s real status arrives through the
hourly marketplace sync; poll the offer to follow it.
Prerequisites
- An Oracle Marketplace integration connected at the organization level.
- The product synced into Fours from your OCI listing — Fours resolves the listing OCID from the product’s external ID, so a product created by hand will not work. See List a Product.
- The buyer’s tenancy OCID, or an existing Fours buyer record whose partner is
ORACLE. - A contract document (EULA) reachable over
https, or already uploaded to Fours. - An access token — see OAuth App.
Endpoint
POST https://api.suger.cloud/org/{orgId}/offer
| Operation ID | CreateOffer |
| Auth | Authorization: Bearer <access_token> |
| Body | A WorkloadOffer object |
| Success | 200 with the created WorkloadOffer |
Request fields
Top level
| Field | Required | Notes |
|---|---|---|
partner | ✅ | ORACLE |
service | ✅ | MARKETPLACE |
offerType | ✅ | PRIVATE. Oracle supports no other type — CPPO_OUT and channel offers are rejected. |
productID | ✅ | Fours product ID. Must be an Oracle-synced product carrying the listing OCID. |
name | ✅ | Becomes the offer’s displayName in OCI. Oracle rejects an empty name. |
buyerID | ⬜ | A Fours buyer to link. Required only if you omit info.oracleOffer.buyerTenancyOCID; the buyer must belong to partner ORACLE. |
info
| Field | Required | Notes |
|---|---|---|
info.eulaUrl | ✅ | The contract document. Either an https URL that Fours fetches server-side, or the key of a file you uploaded to Fours. Plain http is rejected. Must resolve to a PDF of at most 1MB (Oracle’s per-attachment limit). |
info.additionalEulaUrls | ⬜ | Further contract documents, same format and limits as info.eulaUrl. Each becomes its own attachment on the OCI offer (unlike AWS, where additional EULAs are merged into one document). |
info.currency | ⬜ | ISO-4217 code. Defaults to USD. |
info.commits | ⬜ | The offer’s line items. Their total becomes the offer’s totalAmount — see Pricing below. |
info.oracleOffer
Oracle-specific fields, an OracleMarketplaceOffer object.
| Field | Required | Notes |
|---|---|---|
sellerPrimaryContact | ✅ | Must include email. Oracle fails the send with SELLER_INFORMATION_IS_NULL without it. |
buyerPrimaryContact | ✅ | Must include email. Oracle rejects the send without a buyer contact. |
buyerTenancyOCID | ✅* | The buyer’s OCI tenancy OCID. *Required unless you set buyerID to a linked Oracle buyer; when both are present, this field wins. |
buyerCompanyName | ⬜ | Buyer’s company name. |
description | ⬜ | Offer description shown in OCI. |
duration | ⬜ | ISO-8601 contract length (P1Y, P6M, P30D, …), measured from timeStartDate — or from the creation time when billing starts on acceptance. Defaults to P1Y (one year). Oracle accepts only day-based durations, so Fours converts the value to days before sending: P1Y goes out as P365D (or P366D across a February 29). Oracle’s maximum total contract length is 375 days, so P2Y and P3Y are rejected at send time. |
timeEndDate | ⬜ | An explicit end date, used instead of duration when both are set. Fours converts it to a day count from the start, rounding partial days up. Must be after timeStartDate, or in the future when billing starts on acceptance. |
timeStartDate | ⬜ | When billing starts. Omit it to start billing when the buyer accepts — the default. |
timeAcceptBy | ⬜ | Deadline for the buyer to accept. Defaults to 30 days out, or to the day before timeStartDate if that is sooner. Must be before timeStartDate — Oracle rejects the send otherwise. |
Contact objects take firstName, lastName, and email.
Pricing
Oracle offers are sent to OCI with a single ONE_TIME billing cycle and one total amount. Fours
computes that total from info.commits:
totalAmount = round( Σ (commit.rate × commit.quantity) )
- A commit with no
quantitycounts as1. - The sum is rounded to the nearest whole unit of currency, never truncated.
- Omit
info.commitsentirely and the offer is created with a total of0— which Oracle rejects at send time, because its minimum contract value is 10,000 USD.
Example
Always include info.commits. Oracle requires a minimum contract value of 10,000 USD, so an offer whose
commits sum to less than that — including an offer with no info.commits at all, which Fours sends as a $0
total — is rejected by Oracle at send time and ends up as CREATE_FAILED. Usage reported on the resulting
entitlement is billed in addition to this contract value, not instead of it; see
Usage Metering.
curl -L -X POST 'https://api.suger.cloud/org/YOUR_ORG_ID/offer' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data-raw '{
"partner": "ORACLE",
"service": "MARKETPLACE",
"offerType": "PRIVATE",
"productID": "KVvfb0Vhv",
"name": "Acme Corp - Annual Contract 2026",
"info": {
"currency": "USD",
"eulaUrl": "https://acme.example.com/legal/eula-2026.pdf",
"additionalEulaUrls": [
"https://acme.example.com/legal/annex-a-2026.pdf"
],
"commits": [
{ "name": "Platform subscription", "rate": 50000, "quantity": 1 }
],
"oracleOffer": {
"description": "Annual platform subscription for Acme Corp.",
"duration": "P1Y",
"buyerTenancyOCID": "ocid1.tenancy.oc1..aaaaaaaaexamplebuyertenancy",
"buyerCompanyName": "Acme Corp",
"buyerPrimaryContact": {
"firstName": "Ada",
"lastName": "Lovelace",
"email": "[email protected]"
},
"sellerPrimaryContact": {
"firstName": "Grace",
"lastName": "Hopper",
"email": "[email protected]"
}
}
}
}'
The response is the persisted offer. Note status — it is the Fours record, not yet the OCI state:
{
"id": "FIwCbefqu",
"organizationID": "YOUR_ORG_ID",
"partner": "ORACLE",
"service": "MARKETPLACE",
"offerType": "PRIVATE",
"productID": "KVvfb0Vhv",
"buyerID": "gHYis9RKU",
"name": "Acme Corp - Annual Contract 2026",
"status": "DRAFT",
"info": {
"currency": "USD",
"visibility": "PRIVATE",
"deliveryMethod": "PRIVATE"
}
}
Keep the returned id. It is the Fours offer ID used for every follow-up call, and it doubles as the
idempotency token for the OCI create — a retried submission will not produce a duplicate
buyer-visible offer.
Track the offer
curl -L -X GET 'https://api.suger.cloud/org/YOUR_ORG_ID/offer/FIwCbefqu' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Immediately after the 200, Fours moves the offer to PENDING_CREATE while it creates the offer in
OCI, attaches the contract, and sends it — about five minutes. Once OCI has the offer,
info.oracleOffer.offerID holds the private-offer OCID and info.oracleOffer.offerStatus holds
Oracle’s raw status. Fours maps that raw status onto its own:
Oracle offerStatus | Fours status |
|---|---|
DRAFT | DRAFT |
PENDING_MARKETPLACE | PENDING_MARKETPLACE_APPROVAL |
PENDING_BUYER | PENDING_ACCEPTANCE |
ACCEPTED | ACCEPTED |
ACTIVE | ACTIVE |
ENDED, EXPIRED | EXPIRED |
FAILED_SEND, FAILED_ACCEPT | CREATE_FAILED |
When Oracle rejects the send, info.oracleOffer.lifecycleDetails carries Oracle’s reason — for example
DURATION_IS_INVALID or TIME_ACCEPT_BY_IS_INVALID — and the offer’s metaInfo.errorMessages records it
as … send rejected (FAILED_SEND: <reason>).
The Fours-side lifecycle, from the synchronous response through the hourly sync:
As soon as the offer is ACCEPTED, Fours derives an entitlement for the buyer — ACTIVE straight away
when the offer has no timeStartDate (billing starts on acceptance); otherwise PENDING_START until the
offer’s start date, ACTIVE from then on. Buyers are deduped by tenancy OCID, so repeat deals with the same
customer attach to the same buyer record.
Withdraw an offer
curl -L -X POST 'https://api.suger.cloud/org/YOUR_ORG_ID/offer/FIwCbefqu/cancel' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Oracle allows withdrawing any offer the buyer has not yet accepted, so this is valid while the offer is
PENDING_ACCEPTANCE or PENDING_MARKETPLACE_APPROVAL. Once accepted, it can no longer be withdrawn.
Validation errors
These are returned as 400 before anything is created in OCI, so a rejected request never leaves an
orphaned draft in your marketplace account.
| Message | Fix |
|---|---|
invalid offer type ... to create private offer in Oracle Marketplace | Set offerType to PRIVATE. |
offer name is required | Set name. |
info.eulaUrl is required | Provide a contract document. |
info.eulaUrl ... must be an https URL | Use https, not http. |
info.oracleOffer.sellerPrimaryContact (with email) is required | Add the seller contact with an email. |
info.oracleOffer.buyerPrimaryContact (with email) is required | Add the buyer contact with an email. |
buyer tenancy OCID is required ... | Set info.oracleOffer.buyerTenancyOCID, or link an Oracle buyer via buyerID. |
info.oracleOffer.timeAcceptBy (...) must be before timeStartDate (...) | Move timeAcceptBy before timeStartDate, or omit it to take the default. |
info.oracleOffer.timeEndDate (...) must be after timeStartDate (...) | Move timeEndDate after the start, or send a duration instead. |
info.oracleOffer.timeEndDate (...) must be in the future when billing starts on acceptance | Use a future end date, or send a duration instead. |
info.oracleOffer.duration "..." is not a valid ISO-8601 period ... | Send a period such as P1Y, P6M or P30D. |
info.oracleOffer.duration "..." must be a non-zero period | Send a positive period, or omit duration for the one-year default. |
Two further checks need OCI context and therefore surface after the API has returned, as a
CREATE_FAILED offer rather than a 400:
- The product must resolve to an Oracle listing OCID.
- A linked
buyerIDmust belong to partnerORACLE; a buyer imported from another marketplace is refused.
Spotted something wrong or out of date on this page? Tell us and we'll correct it.