Datacove API Developer guides
AWS Marketplace · Seller integration

AWS Marketplace guide

For the teams that list a Datacove product on AWS Marketplace and build the tenant website's signup page. It covers the AWS seller account, the platform settings, the buyer's first key, how buyers call the API, and how usage and revenue reach AWS and our reports.

Base URLhttps://<api-host>/api
Buyer credentialmk_ API key
User roleMARKETPLACE_USER
BillingBy AWS, in USD

01How it works

A marketplace product belongs to one tenant and lists the workflows its buyers may run. Each AWS buyer becomes a subscription under that product, holding its own mk_ API keys. AWS bills the buyer; Datacove reports each buyer's usage to AWS every hour.

On AWSBuyer subscribesthen chooses Set up your account
DatacoveFulfillment URLidentifies the buyer with AWS
Tenant websiteSignup pagecontact details, then the key
BuyerCalls the APIx-api-key: mk_…

Two ways to price a product

Pay as you go: a price per successful call, reported to AWS as usage. Plans: AWS contracts with a price per term and a call allowance (or unlimited); when the allowance runs out, calls are either refused or billed as overage. A product uses exactly one of the two, chosen when it is created.

Two ways to hand over the key

Redirect (AWS "SaaS product"): the buyer reveals the key once on the tenant's signup page. Quick Launch (AWS "API-Based Agents & Tools"): the same signup, and the key is also stored in the buyer's own AWS Secrets Manager. AWS fixes this per listing, so it is set per product.

Subscription statusMeaning
pending_registrationAWS has told us about the buyer, but they haven't finished the signup page.
pending_activationSignup is done; waiting for AWS to confirm the subscription. Usually seconds.
activeKeys work.
suspendedKeys are blocked: by an administrator, or by an AWS payment advisory.
cancel_pendingThe buyer is leaving. Keys are blocked and final usage is reported straight away.
endedThe AWS subscription is over. Keys never work again.
failedAWS could not set the subscription up (for example a payment failure).

02AWS seller setup

Done once per seller AWS account, in us-east-1, where the AWS Marketplace APIs run. Every value below is saved on the tenant (section 03), never in server configuration, because each tenant can sell from its own account.

Event queue

  1. Create a standard SQS queue (not FIFO), plus a second one as its dead-letter queue with maximum receives 5. Visibility timeout 60 seconds, retention 4 days or more. The backend reads it every minute and deletes each message once stored.
  2. Give the queue an access policy that lets SNS and EventBridge deliver to it:
SQS access policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowMarketplaceSns",
      "Effect": "Allow",
      "Principal": { "Service": "sns.amazonaws.com" },
      "Action": "sqs:SendMessage",
      "Resource": "arn:aws:sqs:us-east-1:<seller-account-id>:<queue-name>",
      "Condition": { "ArnEquals": { "aws:SourceArn": [
        "arn:aws:sns:us-east-1:<aws-owned-account>:aws-mp-subscription-notification-<product-code>"
      ] } }
    },
    {
      "Sid": "AllowMarketplaceEventBridge",
      "Effect": "Allow",
      "Principal": { "Service": "events.amazonaws.com" },
      "Action": "sqs:SendMessage",
      "Resource": "arn:aws:sqs:us-east-1:<seller-account-id>:<queue-name>",
      "Condition": { "ArnEquals": {
        "aws:SourceArn": "arn:aws:events:us-east-1:<seller-account-id>:rule/<rule-name>"
      } }
    }
  ]
}

SNS topics (older products)

If the listing shows an SNS topic, subscribe the queue to it (protocol Amazon SQS). For a plans product, also subscribe to the aws-mp-entitlement-notification-<product-code> topic if listed. Raw message delivery can be on or off.

EventBridge rule (all products)

On the default event bus, create a rule with the pattern { "source": ["aws.agreement-marketplace"] } and the queue as its target, passing the full event. Receiving the same change from SNS and EventBridge is harmless; each message is stored once.

Credentials

Create one IAM policy and attach it either to an IAM user whose access keys are saved on the tenant, or to a role the backend assumes with an external ID.

IAM policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "MarketplaceApis",
      "Effect": "Allow",
      "Action": [
        "aws-marketplace:ResolveCustomer",
        "aws-marketplace:BatchMeterUsage",
        "aws-marketplace:GetEntitlements",
        "aws-marketplace:DescribeAgreement",
        "aws-marketplace:GetAgreementTerms",
        "aws-marketplace:DescribeEntity",
        "aws-marketplace:PutDeploymentParameter"
      ],
      "Resource": "*"
    },
    {
      "Sid": "EventQueue",
      "Effect": "Allow",
      "Action": ["sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes"],
      "Resource": "arn:aws:sqs:us-east-1:<seller-account-id>:<queue-name>"
    },
    {
      "Sid": "BillingDataFeed",
      "Effect": "Allow",
      "Action": ["s3:ListBucket", "s3:GetObject"],
      "Resource": ["arn:aws:s3:::<feed-bucket>", "arn:aws:s3:::<feed-bucket>/*"]
    }
  ]
}
PermissionUsed for
ResolveCustomerIdentifying the buyer at the fulfillment URL.
BatchMeterUsageHourly usage reports (pay as you go and overage).
GetEntitlementsReading a plans buyer's contract tier.
DescribeAgreement, GetAgreementTermsMatching agreement events to a product, and the buyer's agreed prices for revenue.
DescribeEntityChecking a product against its listing.
PutDeploymentParameterQuick Launch products: storing the key in the buyer's AWS account.
s3:ListBucket, s3:GetObjectOptional billing reconciliation from the seller data feed.

Listing settings

The listing and the Datacove product must agree on these, or usage is rejected by AWS or priced wrongly in reports.

Listing fieldSet to
Fulfillment URLThe product's fulfillmentUrl, copied exactly as the API returns it (section 03). It already includes any deployment path prefix.
Pricing modelPay as you go: "SaaS subscriptions" with one usage dimension. Plans: "SaaS contracts" with one tier per plan, "with pay-as-you-go" when the product bills overage.
Dimension API identifiersExactly the product's dimensionKey values: the pay-as-you-go dimension, each plan's dimension, and the overage dimension.
Prices and durationsThe same prices, and each plan's durationMonths (1, 12, 24 or 36). AWS charges what the listing says; the product's copy drives allowances, the signup page and revenue estimates.
Test before publishing

While the listing is limited, allow a test AWS account, subscribe from it and choose Set up your account. You should land on the signup page with ?session=, finish signup, reveal the key and make a call. A few minutes after the next full hour the usage report shows sent (section 07). Use a nominal price while testing.

Billing data feed (optional)

To replace revenue estimates with what AWS actually invoiced, subscribe the seller account to AWS Marketplace seller data feeds (AWS Marketplace Management Portal → Set up customer data storage), delivered to an encrypted S3 bucket in the seller account. Save the bucket on the tenant as dataFeedBucket, with dataFeedPrefix if the feeds live under a key prefix. The billing event feed is then imported every day (section 06).

03Platform setup

Both steps are SUPER_ADMIN calls, usually made from the admin panel.

Tenant settings

PATCH/tenants/v1/:id/aws-marketplace-config SUPER_ADMINSecrets are encrypted at rest

Partial update; send only what changes. GET on the same path returns the settings with secrets replaced by has… flags. Setting isEnabled: true is refused until the credentials for the chosen mode, queueUrl and signupPageUrl are all set.

FieldTypeNotes
isEnabledbooleanTurns marketplace on for this tenant.
sellerAccountIdstringThe seller AWS account, for reference.
regionstringWhere the queue and feed bucket live. Default us-east-1.
credentialMode"access_keys" | "assume_role"How the backend authenticates to the seller account.
accessKeyId, secretAccessKeystringaccess_keys mode.
roleArn, externalIdstringassume_role mode.
queueUrlhttps URLThe event queue from section 02.
signupPageUrlhttps URLThe tenant website page that finishes signup (section 04).
supportEmailemailShown to buyers on the signup page.
dataFeedBucket, dataFeedPrefixstringOptional billing reconciliation. An empty string turns it off.

Products

POST/marketplace/products/v1 SUPER_ADMIN

Creates the product and returns it with its fulfillmentUrl, the address to paste into the listing. pricing.type can't change later; create a new product (and listing) to switch.

Pay as you go
{
  "tenantId": "<tenant id>",
  "name": "URL Threat Check API",
  "externalProductId": "prod-xxxxxxxxxxxx",
  "productCode": "<product code>",
  "fulfillmentMode": "redirect",
  "notificationChannel": "eventbridge",
  "pricing": {
    "type": "pay_as_you_go",
    "payAsYouGo": { "dimensionKey": "api_call", "pricePerCallUsd": 0.05 }
  },
  "allowedWorkflows": ["wf-sec-001"],
  "keyLifetimeDays": 365
}
Plans
"pricing": {
  "type": "plans",
  "plans": [{
    "key": "starter",
    "name": "Starter",
    "dimensionKey": "starter_tier",
    "priceUsd": 499,
    "durationMonths": 12,
    "callLimit": 10000,
    "resetEvery": "monthly"
  }],
  "onLimit": {
    "action": "overage",
    "dimensionKey": "overage_call",
    "pricePerCallUsd": 0.08
  }
}
FieldNotes
externalProductId, productCodeFrom the listing. The product id starts with prod-.
fulfillmentModeredirect for an AWS "SaaS product", quick_launch for "API-Based Agents & Tools".
notificationChannelsns if the listing shows an SNS topic, otherwise eventbridge.
allowedWorkflows, allowedWorkflowCategoriesWhat buyers may run. "*" allows everything the tenant offers.
callLimitnull for unlimited. resetEvery is monthly or term.
onLimit.actionblock refuses calls once the allowance is used; overage bills them on the overage dimension.
keyLifetimeDaysOptional expiry for buyer keys. Omit for keys that don't expire.

Also available: GET /marketplace/products/v1/:pageNo/:pageSize, GET/PATCH/DELETE /marketplace/products/v1/:id, and POST /marketplace/products/v1/:id/aws-check, which compares the product with its listing (dimensions and prices) and is the quickest way to prove the credentials work.

For the buyer API docs (section 04) to show what each workflow returns, set its optional responseExample (one real successful response) and/or responseSchema (a JSON Schema of it) with PATCH /workflows/v1/:id (SUPER_ADMIN or DEVELOPER). Both are descriptive only; they never change what a run returns. Send null to remove one.

04Tenant signup page

The tenant website owns the page and its design. AWS posts the buyer to the fulfillment URL; the backend identifies them with AWS and redirects the browser to signupPageUrl with either ?session=<token> or ?error=<code>. The page then talks to the API with the tenant website's own dk_ key.

?error=What to show
invalid_tokenThe AWS link expired or was already used. Ask the buyer to choose Set up your account again in AWS Marketplace.
product_mismatchThe subscription is for a different product. Ask the buyer to start again from the right listing.
product_unavailableThe product is not open for signups. Show the support email.
setup_failedA temporary problem. Ask the buyer to try again shortly.
LoadRead the sessionGET …/sessions/:token
FormComplete signupPOST …/sessions/:token/complete
When activeReveal the keyPOST …/sessions/:token/key
GET/marketplace/signup/v1/sessions/:sessionToken dk_ keyValid for 30 minutes

Everything the page renders: product name, pricing, subscription status, whether signup is done, and whether the key can be revealed. While status is pending_activation, poll every few seconds until it becomes active.

Response · 200
{
  "productName": "URL Threat Check API",
  "pricing": { "type": "pay_as_you_go", "payAsYouGo": { "dimensionKey": "api_call", "pricePerCallUsd": 0.05 } },
  "status": "pending_activation",
  "isRegistered": true,
  "isFreeTrial": false,
  "contact": { "name": "Priya Sharma", "email": "priya@example.com", "company": "Example Corp" },
  "hasKey": false,
  "canRevealKey": false,
  "supportEmail": "support@example.com",
  "expiresAt": "2026-10-15T10:30:00.000Z"
}
POST…/sessions/:sessionToken/complete

Body { "name", "email", "company" } (each required, up to 200 characters). Returns the session again. 409 SUBSCRIPTION_CLOSED if the AWS subscription already ended or failed.

POST…/sessions/:sessionToken/key

Issues the buyer's first key and returns { apiKey, apiKeyId, expiresAt } once. Show it with a copy button and a "won't be shown again" warning. 409 SUBSCRIPTION_NOT_ACTIVE until AWS confirms; 409 KEY_ALREADY_ISSUED if a working key exists.

Quick Launch products

The page works the same way. Once the key is issued, it is also stored in the buyer's AWS Secrets Manager under the name api-key, as soon as AWS has sent the buyer's agreement. Tell the buyer they can return to AWS Marketplace to finish launching.

Publishing API docs

The tenant website can also host the buyers' API reference, generated live from the product, so it never goes stale. Fetch the document from the website's server with the same dk_ key and render it with any OpenAPI viewer (Redoc, Scalar, Swagger UI). Buyer keys and other callers are refused.

GET/marketplace/docs/v1/openapidk_ key

An OpenAPI 3.0 document. Query product (the product's publicRef) is optional when the tenant sells one product; otherwise 400 PRODUCT_REQUIRED lists the choices in error.products. 404 for a product the tenant doesn't sell. Cached for 30 minutes.

GET/marketplace/docs/v1/productsdk_ key

The tenant's active products: [{ publicRef, name, pricingType }], for a page that offers more than one.

The document containsBuilt from
POST /workflows/v1/execute, with a request shape and example per workflowThe workflows the product allows: their names, input schemas and placeholders, and response examples or schemas where set.
One POST /workflows/v1/tools/{workflowId} per workflowThe same workflows, as agent tools.
The agent spec, key rotation, and report polling (only when a workflow returns a report)The live API definitions.
servers, the x-api-key security scheme, the error shapeThe deployment's public address and the platform's error envelope.
x-marketplace: productName, publicRef, pricing, supportEmail, workflowsThe product's listing prices and the tenant's support email, for the page around the reference.

Workflows that need a file upload are left out, as they are from the agent spec. Point the signup page's "API docs" link at the page that renders this document.

05Calling the API as a buyer

Buyers send their key as x-api-key: mk_… on every call, from their own servers or an agent gateway. There is no token exchange and no browser origin check. The key works only while the subscription is active.

Running workflows

POST/workflows/v1/execute mk_ key

The same call every API-key integration uses: { "id": "<workflowId>", "input": { … } }. Only the product's workflows are allowed; anything else is 404. Each successful run is billed; a failed run is not, and gives back its plan allowance.

cURL
curl -X POST "$BASE_URL/workflows/v1/execute" \
  -H "x-api-key: mk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "id": "wf-sec-001", "input": { "url": "https://example.com/login" } }'
Long runs
# Queue it, then poll the report every 5 s
curl -X POST "$BASE_URL/workflows/v1/execute?callAsync=true" …
# → { "success": true, "eventId": "…" }
curl "$BASE_URL/workflows/reports/v1/<eventId>" -H "x-api-key: mk_live_…"

Use callAsync=true for workflows that take minutes, so the request isn't cut off by a network timeout. The report poll answers { "status": "GENERATING" } while running, then the result. A buyer can only read its own reports.

Agent tools

GET/workflows/v1/agent-specmk_ key

An OpenAPI document with one tool per workflow in the product, each with a short operationId (for example run_url_threat_check) and its own typed request body. Workflows that need a file upload are left out. Load it into an agent gateway such as AWS Bedrock AgentCore and configure the x-api-key header there; the spec itself carries no auth.

POST/workflows/v1/tools/:workflowIdmk_ / sk_ key

What each tool calls: the workflow's inputs are the body itself, with no { id, input } wrapper. JSON only. Returns 200 with the result. Billing, allowances and errors are the same as for execute.

Sellers get the same document for the listing from GET /marketplace/products/v1/:id/agent-spec (SUPER_ADMIN).

Rotating a key

POST/marketplace/v1/keys/rotate mk_ keyOld key keeps working 24 h

No body. Authenticated by the key being replaced; returns a new { apiKey, apiKeyId, expiresAt } once. The old key keeps working for 24 hours (or until its own expiry, if sooner) so the buyer can switch over. At most 5 keys at a time (409 TOO_MANY_KEYS). For Quick Launch products the new key also replaces the one in the buyer's Secrets Manager. A buyer who lost their key asks support, who reissue one (section 07).

06Metering and revenue

Hourly usage

A few minutes past each hour, the previous hour's successful billable runs are reported to AWS with BatchMeterUsage, one record per buyer and dimension. A report is never sent twice. AWS refuses usage 24 hours or more old, so failures are retried within that window.

Estimated revenue

Accepted usage is added to one payment per buyer per UTC day, and each plan term bought to one payment, priced at the buyer's agreed rate from AWS (or the product's list price). These payments have provider aws_marketplace, currency USD and status estimated.

Reconciled revenue

With the billing data feed set up, each AWS invoice becomes one payment: pending when invoiced, completed once AWS collects it, refunded if forgiven, with AWS's listing fee in metadata.listingFee. The estimates it covers become superseded.

Dashboards show marketplace revenue in a separate USD bucket, and workflow stats show marketplace runs and revenue in their own block; neither is ever mixed into INR or CAD earnings. When an invoice replaces estimates, the dashboard is corrected by the difference, so nothing is counted twice.

07Operations

SUPER_ADMIN endpoints for support and finance. Every change is written to the audit log.

CallUse
GET /marketplace/subscriptions/v1/:pageNo/:pageSizeBuyers. Filters tenantId, productId, status, search (email, company or AWS account).
GET /marketplace/subscriptions/v1/:idOne buyer: status history, key metadata (never the key), Quick Launch delivery, and a usage summary (plan allowance, 30-day runs, units reported, revenue).
POST …/subscriptions/v1/:id/suspendBody { "reason" }. Blocks the keys at once. AWS keeps billing any contract.
POST …/subscriptions/v1/:id/restoreLifts a suspension, including one from an AWS advisory.
POST …/subscriptions/v1/:id/keysReissues a lost key: every existing key stops, the new one is returned once.
POST …/subscriptions/v1/:id/keys/deliverQuick Launch: resends the newest key to the buyer's AWS account. Delivery is otherwise automatic and retried every 10 minutes.
GET /marketplace/events/v1/:pageNo/:pageSizeEvery AWS notification received and how it was handled. Failed ones retry up to 5 times.
GET /marketplace/metering/v1/:pageNo/:pageSizeUsage reports per buyer, dimension and hour, with AWS's error for failed ones.
POST /marketplace/metering/v1/:id/retryRe-queues a failed report while AWS still accepts it (within 23 hours).
GET /marketplace/billing/v1/:pageNo/:pageSizeImported AWS billing events. unmatched=true lists rows for an account or product no buyer matches.
POST /marketplace/billing/v1/importBody { "tenantId" }. Runs the daily billing feed import now. Safe to repeat.
SymptomCheck
The signup page stays on "waiting for AWS"No subscribe event arrived: the SNS subscription or EventBridge rule, the queue policy, and the event log.
Events show ignored with "no marketplace product configured"The product code or prod- id on the product doesn't match the listing.
Usage reports failDimension keys must match the listing exactly; CustomerNotSubscribed means the buyer left on AWS.
Quick Launch key not deliveredThe subscription's keyDelivery.lastError; a missing PutDeploymentParameter permission is the usual cause.

08Errors

Errors use the platform envelope. Branch on the status, then on error.code.

Error envelope
{
  "statusCode": 429,
  "timestamp": "2026-10-15T09:30:00.000Z",
  "path": "/api/workflows/v1/execute",
  "error": {
    "code": "PLAN_LIMIT_REACHED",
    "message": "Your plan's call allowance is used up. It resets on 2026-11-01T00:00:00.000Z.",
    "resetsAt": "2026-11-01T00:00:00.000Z"
  }
}
Statuserror.codeWhereWhat to do
401—any buyer callThe key is wrong, expired, rotated out, or the subscription isn't active.
404—execute, toolsThe workflow isn't part of this product.
429PLAN_LIMIT_REACHEDexecute, toolsThe plan allowance is used up; it resets at resetsAt. Plain 429s without a code are rate limits.
409TOO_MANY_KEYSrotateFive keys exist. Ask support to remove old ones.
404SESSION_NOT_FOUNDsignupThe session is unknown or older than 30 minutes. The buyer starts again from AWS.
409SUBSCRIPTION_CLOSEDsignup completeThe AWS subscription ended or failed.
409SUBSCRIPTION_NOT_ACTIVEsignup keyAWS hasn't confirmed yet. Keep polling the session.
409KEY_ALREADY_ISSUEDsignup keyThe buyer already has a working key.
502—execute, toolsThe workflow service failed. Safe to retry; failed runs are never billed.