developer

Generated OpenAPI reference

API for managing A/B testing experiments, variations, goals, webhooks, and screenshots. Every v1 response carries X-Apex-Cli-Minimum and X-Apex-Cli-Latest version-policy headers. Write requests from an Apex CLI below the enforced minimum answer 426; read requests keep working. Landing-page bundles allow 300 files and 100 MB per version, including MP4/WebM up to 50 MB per video (3 MB per other file). Filesystem/upload-grant issues a 15-minute, version-bound grant for direct Worker multipart upload above 3 MB.

Assignments

GET

Get deterministic server-side assignments

/assign

Parameters

  • shop_id in query required
  • user_id in query required
  • url in query - Browser URL for storefront URL targeting. App clients may send an app screen URL (app://<bundle id>/<screen>); app callers (publishable-key lane or any app:// URL) are never assigned experiments with storefront URL targeting.
  • environment in query
  • experiments in query

Responses

  • 200 Assignments and program cohort returned
  • 400 Invalid request, including a customer identifier supplied in the query string instead of a POST body.
  • 401
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/assign \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Get deterministic assignments with commerce proof inputs

/assign

Request body

{
  shop_id: string
  user_id?: string
  logged_in_customer_id?: string
  customer_id?: string
  decision_use?: "aggregate" | "individual"
  url?: string (uri)
  environment?: string
  experiments?: string[]
  cart_token?: string
  persist_function_handoff?: boolean
  exclusion_group_selections?: object[]
}

Responses

  • 200 Assignments, program cohort, and commerce authorization returned
  • 400
  • 401
  • 403 A logged-in customer assignment was requested for an individual-level decision.
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/assign \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_id": "example_shop_id"
}'

Runtime

POST

Verify a signed cross-domain bucketing handover token

/handover/verify

Worker runtime endpoint. Verifies the token signature, expiry, and shop binding without requiring ingest credentials.

Parameters

  • X-Drip-Shop in header required

Request body

{
  verifyToken: string
}

Responses

  • 200 Verified handover payload
  • 400 Missing or invalid shop identifier
  • 401 Unsigned, forged, expired, or cross-shop handover token
  • 500 Handover verification is unavailable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/handover/verify \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "verifyToken": "example_verifyToken"
}'

Other

POST

Inspect experiment conflicts

/experiments/conflicts

Request body

{
  selectors: string[]
  exclude_experiment_id?: string (uuid)
}

Responses

  • 200 Conflict analysis
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/conflicts \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "selectors": [],
  "exclude_experiment_id": "uuid-here"
}'
PATCH

Update experiment QA

/experiments/{id}/qa

Approves QA and marks ready to launch, without launching or changing weights. Zero-traffic acknowledgement requires an Admin or Internal person principal; shop API keys cannot acknowledge.

Parameters

  • scope in query - Set to store to act only on this store; omitted acts on every store when this is a brand-test parent.

Request body

{
  acknowledge_degenerate_weights?: boolean
  acknowledge_degenerate_weights_hash?: string
  scope?: "store"
}

Responses

  • 200 QA updated: the raw experiment row, plus program on a brand-test parent (#9255)
  • 400
  • 401
  • 403
  • 404
  • 409 QA conflict, including a missing, stale, or unauthorized zero-traffic acknowledgement
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "acknowledge_degenerate_weights": false,
  "acknowledge_degenerate_weights_hash": "example_acknowledge_degenerate_weights_hash",
  "scope": "store"
}'
DELETE

Reset experiment QA

/experiments/{id}/qa

Responses

  • 200 QA reset
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read saved commerce QA probe evidence

/experiments/{id}/qa/commerce-probes

Reads saved price, shipping, and post-purchase probe evidence without calling Shopify. Requires experiments:read. Current shipping evidence also returns its saved result, preserving summary, details and optional action: { label: string, href: string }. Results from earlier draft versions are omitted.

Responses

  • 200 Saved commerce probe evidence
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/commerce-probes \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Run commerce QA probes

/experiments/{id}/qa/commerce-probes

The shipping probe checks each enforced arm against the store rate in every delivery zone and also runs for enforced arms without a threshold. Runs the requested saved-draft commerce probes. Requires experiments:write. When shipping evidence cannot be read, kinds.shipping.result includes a plain-language summary and the same sentence first in details. A missing Shopify permission may also include optional action: { label: "Approve in Shopify", href: string }, linking to Pricing & Offers authorization.

Request body

{
  kinds?: ("price" | "shipping" | "post_purchase")[]
}

Responses

  • 200 Commerce probe results and saved evidence
  • 400
  • 401
  • 403
  • 404
  • 409 Experiment must be draft or paused, or installation unavailable
  • 422 Probe not applicable or invalid commerce configuration
  • 429
  • 502 Shopify upstream request failed
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/commerce-probes \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "kinds": []
}'
GET

Read QA-tagged events

/experiments/{id}/qa/events

Returns goal and custom events for a QA session. Requires experiments:read.

Responses

  • 200 QA event groups
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503 QA event readback unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/events \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read QA run

/experiments/{id}/qa/run

Creates a run only when none exists. Existing runs are computed in memory without writes: updatedAt is the stored mutation time; computedAt is the ISO timestamp of this read.

Responses

  • 200 QA run, including QA destination URLs for goals on pages outside experiment targeting (goal-page entries in urls, and goalPageDestinations evidence on the qa_url_coverage checklist item)
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/run \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update QA run

/experiments/{id}/qa/run

Request body

{
  runId: string
  itemId?: string
  status?: "pending" | "pass" | "warning" | "fail" | "accepted" | "skipped"
  notes?: string
  evidence?: object
  runNotes?: string
  acceptanceCriteriaSummary?: string | null
}

Responses

  • 200 QA run updated
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/run \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "runId": "example_runId"
}'
POST

Add a custom QA checklist item

/experiments/{id}/qa/run

Request body

{
  runId: string
  title: string
  description?: string
  category?: string
  required?: boolean
}

Responses

  • 201 QA checklist item created
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/run \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "runId": "example_runId",
  "title": "example_title"
}'
DELETE

Delete custom QA checklist item

/experiments/{id}/qa/run

Parameters

  • runId in query
  • itemId in query

Request body

{
  runId?: string
  itemId?: string
}

Responses

  • 200 QA run deleted
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/run \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "runId": "example_runId",
  "itemId": "example_itemId"
}'
POST

Retired experiment launch endpoint

/experiments/{id}/start

This endpoint is gone. Launch traffic through the dashboard approval workflow.

Responses

  • 410 Launch is dashboard-only
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/start \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create harness session

/harness/sessions

Request body

{
  experimentId?: string
  variationId?: string
  experimentSlug: string
  experimentName: string
  variationSlug: string
  variationName: string
  targetUrl: string (uri)
  resetJs?: string | null
  antiFlickerSelectors?: string[]
  ttlSeconds?: integer
}

Responses

  • 201 Harness session created
  • 400 Invalid request or artifact review blocked the session; nothing was created or changed
  • 401
  • 403
  • 404
  • 409 Artifact conflict during the save, or experiment is not in draft status
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/harness/sessions \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "experimentSlug": "example_experimentSlug",
  "experimentName": "example_experimentName",
  "variationSlug": "example_variationSlug",
  "variationName": "example_variationName",
  "targetUrl": "https://example.com"
}'
POST

Create harness session version

/harness/sessions/{id}/versions

Responses

  • 201 Version created
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/harness/sessions/exp_123/versions \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read platform operator settings

/admin/settings

admin:read plus platform health-section access. Missing undo policy defaults to 7; malformed or unreadable policy returns 503 policy_unavailable.

Responses

  • 200 Typed settings and policy version
  • 503 policy_unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/admin/settings \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Write audited platform operator settings

/admin/settings

admin:write plus platform health-section access. Numeric policy writes require expected_version and reason. All supplied settings and audits commit atomically; boolean-only requests keep their existing contract.

Request body

{
  guardian_enabled?: boolean
  srm_check_enabled?: boolean
  experiment_undo_completion_window_days?: integer
  free_plan_days?: any
  expected_version?: string
  reason?: string
}

Responses

  • 200 Updated typed settings
  • 400 Invalid allowlisted setting, version or reason
  • 403 Insufficient admin or section access
  • 409 policy_changed; no settings written
  • 503 policy_unavailable
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/admin/settings \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

List backlog

/backlog

Parameters

  • limit in query
  • cursor in query
  • include_archived in query - Include archived backlog items when true.

Responses

  • 200 Paginated backlog
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/backlog \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create a backlog item

/backlog

Creates a backlog item. Score and score-provenance fields require an active apex-editor or apex-platform-admin grant on a user API key; shop keys and other users receive 403.

Request body

{ $ref: "BacklogCreate" }

Responses

  • 201 Created
  • 400
  • 401
  • 403
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/backlog \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

List the shop's idea fields

/backlog/fields

The field definitions whose values ride on each idea's custom_fields.

Parameters

  • include_archived in query - Include archived fields when true.

Responses

  • 200 Field definitions, in sheet order
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/backlog/fields \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Add an idea field

/backlog/fields

Request body

{ $ref: "BacklogFieldCreate" }

Responses

  • 201 Created
  • 400
  • 401
  • 403
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/backlog/fields \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
PATCH

Reorder the idea fields

/backlog/fields

Places the live fields. Every named field must be live, and the resulting positions must be unique across the whole live sheet, so send the sheet rather than one row when two fields swap places. The body is the bare array or the same array under an order key.

Request body

any

Responses

  • 200 Field definitions, in the new order
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/backlog/fields \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
PATCH

Update an idea field

/backlog/fields/{id}

Request body

{ $ref: "BacklogFieldUpdate" }

Responses

  • 200 Updated
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/backlog/fields/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Archive an idea field

/backlog/fields/{id}

Archiving keeps the values already stored on ideas. The field disappears from reads and rejects new writes.

Responses

  • 200 Archived
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/backlog/fields/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get a backlogitem

/backlog/{id}

Responses

  • 200 Resource
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/backlog/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update a backlogitem

/backlog/{id}

Updates a backlog item. Score and score-provenance fields, including research_hub_round_trip, require an active apex-editor or apex-platform-admin grant on a user API key; shop keys and other users receive 403. Ordinary metadata replacement remains available when neither stored nor incoming metadata contains protected score or prediction provenance. Omit metadata for ordinary edits on scored items. Supplying protected metadata, including unchanged values, or removing stored provenance returns 403. A concurrent metadata change returns 409 with error.code backlog_item_cas_miss, even when expected_version is omitted. Reload the item and retry.

Request body

{ $ref: "BacklogUpdate" }

Responses

  • 200 Updated
  • 400
  • 401
  • 403
  • 404
  • 409 The supplied version is stale or metadata changed during a non-staff replacement. Reload the item and retry.
  • 422
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/backlog/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Archive a backlog item

/backlog/{id}

Archives the backlog item instead of destroying it.

Responses

  • 200 Deleted
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/backlog/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get an idea's design board

/backlog/{id}/design

Mockups, links, inspiration, and internal build notes, plus every Research Hub mirror the item carries. The internal half (build notes, inspiration, and links whose switch is off) is redacted for a client viewer: a client shop role in the dashboard, and a user API key whose holder holds one. A secret API key is minted with apikeys:manage, which no client role holds, so it reads the whole board.

Responses

  • 200 Design board
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/backlog/exp_123/design \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update an idea's design board

/backlog/{id}/design

Replaces the keys it carries and leaves the rest alone. Arrays replace wholesale.

Request body

{ $ref: "BacklogItemDesignPatch" }

Responses

  • 200 Updated design board
  • 400
  • 401
  • 403
  • 404
  • 409 The idea changed while saving
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/backlog/exp_123/design \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Archive a backlog item and reject its roadmap decision

/backlog/{id}/archive

Request body

{
  feedback?: string | null
  reason?: string | null
}

Responses

  • 200 Archived backlog item
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/backlog/exp_123/archive \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "feedback": "example_feedback",
  "reason": "example_reason"
}'
POST

Approve a backlog idea into a month

/backlog/{id}/approve

Schedules the idea and records who decided. On a shop where DRIP decides the backlog, a client principal is refused with 403.

Request body

{
  roadmap_month: string
  comment?: string | null
}

Responses

  • 200 The idea's decision after approving
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/backlog/exp_123/approve \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "roadmap_month": "example_roadmap_month",
  "comment": "example_comment"
}'
POST

Reject a backlog idea with a reason

/backlog/{id}/reject

Records the rejection and its reason. The idea stays in the backlog, shown as rejected, rather than being archived.

Request body

{
  reason_category: "not_right_for_brand" | "already_tested" | "too_much_effort" | "other"
  reason: string
}

Responses

  • 200 The idea's decision after rejecting
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/backlog/exp_123/reject \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "reason_category": "not_right_for_brand",
  "reason": "example_reason"
}'
POST

Restore an archived backlog item and reset its roadmap decision to proposed

/backlog/{id}/restore

Responses

  • 200 Restored backlog item
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/backlog/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List personalizations

/personalizations

Parameters

  • limit in query
  • cursor in query

Responses

  • 200 Personalizations with cursor pagination
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/personalizations \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create personalization

/personalizations

Request body

{ $ref: "PersonalizationInput" }

Responses

  • 201 Created personalization
  • 400
  • 401
  • 403
  • 404
  • 422 Checkout messages work only on A/B tests.
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/personalizations \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get personalization

/personalizations/{id}

Responses

  • 200 Personalization
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/personalizations/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update personalization

/personalizations/{id}

Request body

{ $ref: "PersonalizationUpdateInput" }

Responses

  • 200 Updated personalization
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/personalizations/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Activate personalization

/personalizations/{id}/activate

Responses

  • 200 Updated personalization
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/personalizations/exp_123/activate \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Pause personalization

/personalizations/{id}/pause

Responses

  • 200 Updated personalization
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/personalizations/exp_123/pause \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Archive personalization

/personalizations/{id}/archive

Responses

  • 200 Updated personalization
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/personalizations/exp_123/archive \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List assets

/assets

Parameters

  • folder in query - Filter to a Brand folder; Brand includes all its subfolders.
  • include_archived in query - Include archived assets.
  • limit in query
  • cursor in query

Responses

  • 200 Assets with cursor pagination
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/assets \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Upload assets

/assets

Responses

  • 201 Uploaded assets
  • 400
  • 401
  • 403
  • 413 Too many files or a file exceeds the upload size limit.
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/assets \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get asset

/assets/{id}

Parameters

  • id in path required

Responses

  • 200 Asset
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/assets/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
DELETE

Archive asset

/assets/{id}

Archives the asset without deleting its stored file; it can be restored.

Parameters

  • id in path required

Responses

  • 200 Deleted
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/assets/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Restore asset

/assets/{id}/restore

Parameters

  • id in path required

Responses

  • 200 Restored asset
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/assets/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read cost settings

/cost-settings

Parameters

  • include_retired in query

Responses

  • 200 Cost settings
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/cost-settings \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Change cost settings

/cost-settings

Request body

{ $ref: "CostSettingsPatchInput" }

Responses

  • 200 Updated cost settings
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/cost-settings \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Read data-deletion status

/data-deletion

Returns the most recent deletion request for this shop that matches the supplied identifier. Supply at least one non-empty identifier: request_id or visitor_id. A request with neither, or with only empty values, returns 400. Both may be supplied; request_id then takes precedence and visitor_id is ignored. No match returns 404.

Parameters

  • request_id in query - Id of a deletion request, as returned by POST. Takes precedence over visitor_id when both are non-empty.
  • visitor_id in query - Visitor id; selects the visitor's most recent deletion request. Used only when request_id is absent or empty.

Responses

  • 200 Deletion status
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/data-deletion \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Request data deletion

/data-deletion

Request body

{
  visitor_id: string
}

Responses

  • 202 The request is recorded when accepted. Deletion usually finishes before the response. While recent event writes for the shop are still settling, it can stay queued for up to about an hour. Copies of erased data that arrive later are found and removed by follow-up checks. Deletion completes within the 30 days promised in the DPA.
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/data-deletion \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "visitor_id": "example_visitor_id"
}'
GET

Export personal data

/data-export

Returns the most recent export request for this shop that matches the supplied identifier. Supply at least one non-empty identifier: request_id or visitor_id. A request with neither, or with only empty values, returns 400. Both may be supplied; request_id then takes precedence and visitor_id is ignored. No match returns 404.

Parameters

  • request_id in query - Id of a export request, as returned by POST. Takes precedence over visitor_id when both are non-empty.
  • visitor_id in query - Visitor id; selects the visitor's most recent export request. Used only when request_id is absent or empty.

Responses

  • 200 Data export
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/data-export \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Request a personal data export

/data-export

Request body

{
  limit?: integer
  visitor_id: string
}

Responses

  • 200 Completed data export
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/data-export \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "limit": 1,
  "visitor_id": "example_visitor_id"
}'
GET

List exclusion groups

/exclusion-groups

Parameters

  • limit in query
  • cursor in query
  • include_archived in query - Include archived exclusion groups.

Responses

  • 200 exclusion groups list
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/exclusion-groups \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create exclusion groups

/exclusion-groups

Request body

{ $ref: "ExclusionGroupInput" }

Responses

  • 201 exclusion groups created
  • 400
  • 401
  • 403
  • 404
  • 409 Retained checkout history refuses membership writes with checkout_mode_retired.
  • 422 Constraint codes: min_members, duplicate_member, invalid_share, totals_mismatch, invalid_experiment, archived_member, or membership_conflict.
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/exclusion-groups \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get exclusion group

/exclusion-groups/{id}

Parameters

  • id in path required

Responses

  • 200 exclusion group returned
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/exclusion-groups/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update exclusion group

/exclusion-groups/{id}

Parameters

  • id in path required

Request body

{ $ref: "ExclusionGroupUpdateInput" }

Responses

  • 200 exclusion group returned
  • 400
  • 401
  • 403
  • 404
  • 409 Retained checkout history refuses membership writes with checkout_mode_retired.
  • 422 Constraint codes: min_members, duplicate_member, invalid_share, totals_mismatch, invalid_experiment, archived_member, or membership_conflict.
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/exclusion-groups/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Archive exclusion group

/exclusion-groups/{id}

Archives the group while preserving membership.

Parameters

  • id in path required

Responses

  • 200 exclusion group deleted
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/exclusion-groups/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Replace exclusion group members

/exclusion-groups/{id}/members

Parameters

  • id in path required

Request body

{ $ref: "ExclusionGroupMembersInput" }

Responses

  • 200 Exclusion group members replaced
  • 400
  • 401
  • 403
  • 404
  • 409 Retained checkout history refuses membership writes with checkout_mode_retired.
  • 422 Constraint codes: min_members, duplicate_member, invalid_share, totals_mismatch, invalid_experiment, archived_member, or membership_conflict.
  • 429
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/exclusion-groups/exp_123/members \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Restore exclusion group

/exclusion-groups/{id}/restore

Parameters

  • id in path required

Responses

  • 200 Exclusion group restored
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/exclusion-groups/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Explain experiment results

/experiments/{id}/results/explain

Diagnostic classifier, not the canonical results pipeline. Labels in precedence order: bot, qa_traffic, outside_window, duplicate, before_exposure, exposed_before_window_only, not_exposed, filtered_by_goal, counted. Residuals expose rule differences between the diagnostic and the results pipeline. Reads are bounded to the result window plus 30 prior days; timeouts never return partial results.

Parameters

  • id in path required
  • goal in query - Attached goal ID or event key; defaults to primary.
  • currency in query
  • order in query - Restrict candidate rows to this order and exposures to its visitors; include its label trail across known arms. Reported values and residuals are omitted (null) in order mode.

Responses

  • 200 Diagnostic explanation
  • 400 Unknown goal or invalid query
  • 401
  • 403
  • 404
  • 409 No result window
  • 422 diagnostic_row_limit: more than 100000 conversion candidates; error.details includes observed row count and a hint to narrow by --order.
  • 429
  • 502 invalid_raw_timestamp; raw analytics data could not be read
  • 503
  • 504 tinybird_timeout; no partial diagnostic
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/results/explain \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Generate external purchase pixel snippet

/external-pixels/snippet

Requires a secret API key with the external_pixels:write scope; the response embeds a signed ingest credential, so read-scoped and publishable keys receive 403 Forbidden.

Request body

{
  globalName?: string = "apexExternalPurchase"
  handoverParam?: string = "drip_bucket"
  provider?: string = "external"
}

Responses

  • 200 Generated snippet
  • 400
  • 401
  • 403
  • 429
  • 500 The server cannot sign the snippet's ingest credential because signing is not configured, or an unexpected server error occurred. Unexpected errors carry error.details.
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/external-pixels/snippet \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "globalName": "example_globalName",
  "handoverParam": "example_handoverParam",
  "provider": "example_provider"
}'
GET

List flags

/flags

Parameters

  • limit in query
  • cursor in query

Responses

  • 200 Flags with cursor pagination
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/flags \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create flag

/flags

Request body

{ $ref: "FeatureFlagInput" }

Responses

  • 201 Created flag
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get flag

/flags/{id}

Parameters

  • id in path required

Responses

  • 200 Flag
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/flags/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update flag

/flags/{id}

Parameters

  • id in path required

Request body

{ $ref: "FeatureFlagUpdateInput" }

Responses

  • 200 Updated flag
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/flags/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Activate flag

/flags/{id}/activate

Parameters

  • id in path required

Responses

  • 200 Updated flag
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/activate \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Archive flag

/flags/{id}/archive

Parameters

  • id in path required

Responses

  • 200 Updated flag
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/archive \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update flag On variant code

/flags/{id}/code

Edits only the On variant code through artifact validation and review. Omitted code is preserved; null clears it. The precondition refers to the On variant. A 502 receipt with saved=true means persistence succeeded but publication failed; do not blindly retry. needs_review is advisory for an already active flag.

Parameters

  • id in path required

Request body

{
  custom_css?: string | null
  custom_js?: string | null
  expected_artifact_hash?: string
  expected_updated_at?: string (date-time)
}

Responses

  • 200 Code saved; publication status is reported separately
  • 400 Validation or artifact review refused the save
  • 401
  • 403
  • 404
  • 409
  • 429
  • 502 Code saved but storefront publication failed
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/flags/exp_123/code \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "custom_css": "example_custom_css",
  "custom_js": "example_custom_js",
  "expected_artifact_hash": "example_expected_artifact_hash",
  "expected_updated_at": "example_expected_updated_at"
}'
POST

Conclude flag with a winning variant

/flags/{id}/conclude

Parameters

  • id in path required

Request body

{
  variant_key: string
}

Responses

  • 200 Concluded flag
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/conclude \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "variant_key": "example_variant_key"
}'
POST

Create a rule experience

/flags/{id}/experiences

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required

Request body

{
  anti_flicker_selectors?: string[]
  code_delivery_mode?: "inline" | "external"
  custom_css?: string | null
  custom_js?: string | null
  environment_key?: string
  expected_updated_at: string (date-time)
  key: string
  mutations?: any[]
  name: string
  reset_js?: string | null
  value?: any
}

Responses

  • 201 Updated flag
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 502 Experience saved but publication failed. The error details preserve the saved receipt.
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/experiences \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_updated_at": "example_expected_updated_at",
  "key": "example_key",
  "name": "example_name"
}'
PATCH

Update a rule experience

/flags/{id}/experiences/{experienceId}

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required
  • experienceId in path required

Request body

{
  anti_flicker_selectors?: string[]
  code_delivery_mode?: "inline" | "external"
  custom_css?: string | null
  custom_js?: string | null
  environment_key?: string
  expected_updated_at: string (date-time)
  key?: string
  mutations?: any[]
  name?: string
  reset_js?: string | null
  value?: any
}

Responses

  • 200 Updated flag
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 502 Experience saved but publication failed. The error details preserve the saved receipt.
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/flags/exp_123/experiences/experience_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_updated_at": "example_expected_updated_at"
}'
DELETE

Delete an unreferenced rule experience

/flags/{id}/experiences/{experienceId}

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required
  • experienceId in path required

Request body

{
  environment_key?: string
  expected_updated_at: string (date-time)
}

Responses

  • 200 Updated flag
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/flags/exp_123/experiences/experience_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "environment_key": "example_environment_key",
  "expected_updated_at": "example_expected_updated_at"
}'
POST

Pause flag

/flags/{id}/pause

Parameters

  • id in path required

Responses

  • 200 Updated flag
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/pause \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List flag QA devices

/flags/{id}/qa-devices

Parameters

  • id in path required

Responses

  • 200 Active QA devices
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/flags/exp_123/qa-devices \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Register or update a flag QA device

/flags/{id}/qa-devices

Parameters

  • id in path required

Request body

{ $ref: "FlagQaDeviceInput" }

Responses

  • 200 Registered QA device
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/qa-devices \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Remove a flag QA device

/flags/{id}/qa-devices/{identity}

Parameters

  • id in path required
  • identity in path required

Responses

  • 200 Removed
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/flags/exp_123/qa-devices/example_identity \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Reopen a concluded flag

/flags/{id}/reopen

Parameters

  • id in path required

Responses

  • 200 Reopened flag
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/reopen \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Replace ordered flag audiences

/flags/{id}/rules

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required

Request body

{
  environment_key?: string
  expected_updated_at: string (date-time)
  rules: object[]
}

Responses

  • 200 Updated flag
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/flags/exp_123/rules \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "environment_key": "example_environment_key",
  "expected_updated_at": "example_expected_updated_at",
  "rules": []
}'
POST

Reorder flag audiences

/flags/{id}/rules/reorder

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required

Request body

{
  environment_key?: string
  expected_updated_at: string (date-time)
  rules: object[]
}

Responses

  • 200 Updated flag
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/flags/exp_123/rules/reorder \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "environment_key": "example_environment_key",
  "expected_updated_at": "example_expected_updated_at",
  "rules": []
}'
GET

Get flag edge publication status

/flags/{id}/status

Returns database publication metadata, the propagation worst-case window derived from the shop's effective runtime cache TTLs, and, for visual flags, the public edge config URL used to confirm propagation. API-delivered flags have no public config URL and are available through /v1/assign.

Parameters

  • id in path required

Responses

  • 200 Flag publication status
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/flags/exp_123/status \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List funnels

/funnels

Parameters

  • limit in query
  • cursor in query
  • include_archived in query - Include archived funnels.

Responses

  • 200 funnels list
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/funnels \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create funnels

/funnels

Request body

{ $ref: "FunnelInput" }

Responses

  • 201 funnels created
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/funnels \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get funnel

/funnels/{id}

Parameters

  • id in path required

Responses

  • 200 funnel returned
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/funnels/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update funnel

/funnels/{id}

Parameters

  • id in path required

Request body

{ $ref: "FunnelUpdateInput" }

Responses

  • 200 funnel returned
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/funnels/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Archive funnel

/funnels/{id}

Archives the funnel instead of destroying it.

Parameters

  • id in path required

Responses

  • 200 funnel deleted
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/funnels/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Restore funnel

/funnels/{id}/restore

Parameters

  • id in path required

Responses

  • 200 Restored funnel
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/funnels/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List landing pages

/landing-pages

Requires the Apex Labs feature for the shop; shops without it receive 404 Not Found.

Parameters

  • limit in query
  • cursor in query
  • status in query

Responses

  • 200 Landing pages with cursor pagination
  • 400
  • 401
  • 403
  • 404 Apex Labs is not enabled for this shop, so landing pages are unavailable.
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/landing-pages \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create landing page

/landing-pages

Creates a landing page to build outside Apex: pull its checkout, apply a bundle, deploy. With idea_key the page records the idea, and experiments created on it link to that idea. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Request body

{
  idea_key?: string
  name: string
  slug?: string
}

Responses

  • 201 Landing page created
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "idea_key": "example_idea_key",
  "name": "example_name",
  "slug": "example_slug"
}'
GET

List landing-page domains

/landing-pages/domains

Requires the landing-pages entitlement for the shop; shops without it receive 404 Not Found. Returns the shop's claimed and registered hostnames, newest first, without removed ones. At most 200; has_more is true when the list was cut. Pass host to look up one hostname.

Parameters

  • host in query - Return only this hostname, canonicalized as the server stores it (lowercase, punycode, no scheme, port or path). Finds a record the 200-row list cut, and a removed record whose Cloudflare hostname delete has not finished (status removed), so DELETE can be retried.

Responses

  • 200 Landing-page domains, newest first
  • 400 host is not a valid hostname (invalid_host)
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/landing-pages/domains \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Claim or register a landing-page domain

/landing-pages/domains

Requires the landing-pages entitlement for the shop; shops without it receive 404 Not Found. mode claim adds a hostname the client controls: point a CNAME at dns_target, then call verify. Claiming the same hostname again returns the existing record (200). mode register buys a new domain on the Drip Cloudflare account, billed to the client on the next DRIP invoice, and needs a user key whose user can write landing pages (owner, admin or editor); Drip staff are notified of every purchase: the first call answers 409 with the quote, and a repeat with confirm_price equal to the quote registers it with auto-renew and WHOIS redaction on. Repeating a register call for a domain this shop already registered resumes its setup and never buys twice.

Request body

{
  confirm_price?: object
  host: string
  mode: "claim" | "register"
  years?: integer = 1
}

Responses

  • 200 Existing record (repeat claim, or resumed registration)
  • 201 Created record with DNS instructions
  • 400
  • 401
  • 403
  • 404
  • 409 Conflict or price confirmation required
  • 429
  • 502 Cloudflare refused or failed the hostname or registrar call: registrar_refused (Cloudflare rejected the order, nothing was charged, the domain can be ordered again) or registrar_failed (the outcome is unknown; run verify)
  • 503 Cloudflare Registrar is not configured (registrar_unconfigured), the token lacks Registrar permission (registrar_permission_missing), or the database is briefly busy (database_busy, with Retry-After)
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/domains \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "confirm_price": {},
  "host": "example_host",
  "mode": "claim",
  "years": 1
}'
DELETE

Remove a landing-page domain

/landing-pages/domains/{id}

Requires the landing-pages entitlement for the shop; shops without it receive 404 Not Found. Deletes the Cloudflare custom hostname and marks the record removed. Refuses with 409 domain_in_use while a published landing page serves the host, and with 409 domain_registration_in_progress while a registration order is unsettled (run verify first). For a charged registration it writes the rebilling audit first and answers 503 domain_audit_pending while that write fails. A registered domain stays registered on the Drip Cloudflare account. Removing a removed record returns it unchanged.

Parameters

  • id in path required

Responses

  • 200 The removed record
  • 401
  • 403
  • 404
  • 409
  • 429
  • 502 Cloudflare could not delete the custom hostname
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/landing-pages/domains/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Check a landing-page domain

/landing-pages/domains/{id}/verify

Requires the landing-pages entitlement for the shop; shops without it receive 404 Not Found. Reads the Cloudflare custom hostname, the registration workflow (mode register) and public DNS, then records the status: pending (no CNAME to the edge yet), verifying (CNAME seen, hostname or certificate not active), active, or failed. For a registered domain it also finishes setup steps an earlier call could not complete; it never orders a domain.

Parameters

  • id in path required

Responses

  • 200 The record with its current status
  • 401
  • 403
  • 404
  • 409
  • 429
  • 502 Cloudflare could not provision the custom hostname
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/domains/exp_123/verify \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get landing page

/landing-pages/{id}

Parameters

  • id in path required

Responses

  • 200 Landing page
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Deploy a landing page version

/landing-pages/{id}/deploy

Publishes a version built outside Apex to the body host or the manifest host (a domain this shop holds), else to the shop's own instant host <shop>.apex-page.com, which the first such deploy assigns; another shop's apex-page.com host is a 400. It upserts its manifest goals and keeps the newest 20 unpublished versions. The Worker injects the shop snippet at serve time unless the manifest turns it off. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{
  host?: string
  path?: string
  version_id?: string (uuid)
}

Responses

  • 200 Deployed
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/deploy \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "host": "example_host",
  "path": "example_path",
  "version_id": "uuid-here"
}'
POST

Create a landing page experiment

/landing-pages/{id}/experiment

Creates a draft experiment that splits the page's live URL between two versions, with the manifest goals attached (the first, or primary_goal, as primary) and the page's idea linked. Starting it is done in the dashboard. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{
  control: integer
  name?: string
  primary_goal?: string
  variant: integer
}

Responses

  • 201 Experiment created
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/experiment \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "control": 1,
  "name": "example_name",
  "primary_goal": "example_primary_goal",
  "variant": 1
}'
GET

Pull a landing page checkout

/landing-pages/{id}/filesystem

Without paths: the current version's apex-landing-page.json, its file list (path, sha256, bytes) and the context folder (brand, catalog, offers, reviews, brief, idea). With paths: those files' contents, utf8 for text and base64 for binary files. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • paths in query - Comma-separated file paths whose contents to return (at most 32 paths and 3145728 bytes). Without it the response lists files and carries the manifest and context.
  • id in path required

Responses

  • 200 Checkout
  • 400
  • 401
  • 403
  • 404
  • 409
  • 413 Requested files exceed one response
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/filesystem \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Apply a landing page bundle

/landing-pages/{id}/filesystem/apply

Stores the bundle as a new version, makes it current, upserts the manifest goals and returns the version and a signed preview link. Files arrive inline, unchanged from the current version, or staged through filesystem/upload. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{
  base_version_id?: string (uuid) | null
  files: object[]
  manifest: object
}

Responses

  • 201 Applied version
  • 400
  • 401
  • 403
  • 404
  • 409
  • 413 Bundle exceeds 300 files or 100 MB
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/filesystem/apply \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "files": [],
  "manifest": {}
}'
POST

Plan a landing page bundle

/landing-pages/{id}/filesystem/plan

Validates a bundle and its manifest and returns what an apply would change (added, changed, removed files, manifest and goal changes, files to upload first). Writes nothing. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{
  base_version_id?: string (uuid) | null
  files: object[]
  manifest: object
}

Responses

  • 200 Plan
  • 400
  • 401
  • 403
  • 404
  • 413 Bundle exceeds 300 files or 100 MB
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/filesystem/plan \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "files": [],
  "manifest": {}
}'
POST

Upload landing page files

/landing-pages/{id}/filesystem/upload

Stages file bytes, keyed by sha256, for a following apply. Upload the files a plan lists under upload_required in batches of at most 32 files and 3 MB. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{
  files: object[]
}

Responses

  • 201 Staged files
  • 400
  • 401
  • 403
  • 404
  • 413 Batch exceeds 32 files or 3 MB
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/filesystem/upload \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "files": []
}'
POST

Grant a direct video upload

/landing-pages/{id}/filesystem/upload-grant

Issues a 15-minute single-file grant bound to shop, page, base version, path, byte size and SHA-256. MP4 and WebM over 3 MB and up to 50 MB upload directly to the Worker using resumable 8 MB multipart parts. Only hash-verified bytes can be applied. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{
  base_version_id: string (uuid) | null
  bytes: integer
  path: string
  sha256: string
}

Responses

  • 201 Video upload grant
  • 400
  • 401
  • 403
  • 404
  • 409 Base version changed or page archived
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/filesystem/upload-grant \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "bytes": 1,
  "path": "example_path",
  "sha256": "example_sha256"
}'
GET

Get a landing page preview link

/landing-pages/{id}/preview

A fresh signed preview link (valid 24 hours, no dashboard login) for the current version, or for the version number in ?version=. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required
  • version in query

Responses

  • 200 Preview link
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/preview \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Roll back a landing page

/landing-pages/{id}/rollback

Deploys an earlier version to the page's current host and path. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{
  version_id?: string (uuid)
  version_number?: integer
}

Responses

  • 200 Deployed
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/rollback \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "version_id": "uuid-here",
  "version_number": 1
}'
GET

List landing page versions

/landing-pages/{id}/versions

Every version, newest first, with its source (generator or bundle), author, size and whether it is current or published. Requires the landing-pages feature for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Responses

  • 200 Versions
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/landing-pages/exp_123/versions \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List pages

/pages

Parameters

  • limit in query
  • cursor in query

Responses

  • 200 pages list
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/pages \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create pages

/pages

Request body

{ $ref: "PageInput" }

Responses

  • 201 pages created
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/pages \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get page

/pages/{id}

Parameters

  • id in path required

Responses

  • 200 page returned
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/pages/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update page

/pages/{id}

Parameters

  • id in path required

Request body

{ $ref: "PageUpdateInput" }

Responses

  • 200 page returned
  • 400
  • 401
  • 403
  • 404
  • 409 Conflict. A live-consumer acknowledgement conflict returns the PageLiveImpactError shape — repeat the request with confirm_live_impact and confirmed_consumer_ids carrying every id listed. A trigger-propagation race or duplicate API name returns the generic Error shape.
  • 422
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/pages/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Delete page

/pages/{id}

Parameters

  • id in path required

Responses

  • 200 page deleted
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/pages/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List segments

/segments

Parameters

  • limit in query
  • cursor in query

Responses

  • 200 segments list
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/segments \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create segments

/segments

Request body

{ $ref: "SegmentInput" }

Responses

  • 201 segments created
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/segments \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
PATCH

Update segment

/segments/{id}

Parameters

  • id in path required

Request body

{ $ref: "SegmentUpdateInput" }

Responses

  • 200 segment returned
  • 400
  • 401
  • 403
  • 404
  • 409 Atomic refusal: a blocked consumer, concurrent edit, or more than 200 consumer expansions in one transaction. Limit refusals use segment_propagation_limit; nothing is saved or queued.
  • 422
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/segments/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Delete segment

/segments/{id}

Parameters

  • id in path required

Responses

  • 200 segment deleted
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/segments/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List signals

/signals

The first page includes the built-in signal catalog before paginated custom signals. Built-ins do not participate in cursor pagination.

Parameters

  • limit in query
  • cursor in query
  • include_archived in query - Include archived custom signals when true. Built-in signals are always active.

Responses

  • 200 Paginated signals
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/signals \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create a signal

/signals

Request body

{ $ref: "SignalCreate" }

Responses

  • 201 Created
  • 400
  • 401
  • 403
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/signals \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get a signal

/signals/{id}

Parameters

  • id in path required - Custom signal UUID or built-in catalog signal key (e.g. url.full).

Responses

  • 200 Resource
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/signals/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update a signal

/signals/{id}

Parameters

  • id in path required - Custom signal UUID or built-in catalog signal key (e.g. url.full).

Request body

{ $ref: "SignalUpdate" }

Responses

  • 200 Updated
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/signals/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Delete or archive a signal

/signals/{id}

Parameters

  • id in path required - Custom signal UUID or built-in catalog signal key (e.g. url.full).

Responses

  • 200 Deleted or archived
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/signals/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List surveys

/surveys

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Parameters

  • limit in query
  • cursor in query
  • status in query

Responses

  • 200 Surveys
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/surveys \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create survey

/surveys

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Request body

{ $ref: "SurveyInput" }

Responses

  • 201 Created survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/surveys \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get survey

/surveys/{id}

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Responses

  • 200 Survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/surveys/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
DELETE

Delete survey

/surveys/{id}

Requires the Surveys lab for the shop; shops without it receive 404 Not Found. Deletes a draft or completed survey; live and paused surveys must be ended first.

Parameters

  • id in path required

Responses

  • 200 Deleted survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/surveys/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update survey

/surveys/{id}

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Request body

{ $ref: "SurveyUpdateInput" }

Responses

  • 200 Updated survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/surveys/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Complete survey

/surveys/{id}/complete

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Responses

  • 200 Survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/surveys/exp_123/complete \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Duplicate survey

/surveys/{id}/duplicate

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Responses

  • 201 Survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/surveys/exp_123/duplicate \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Pause survey

/surveys/{id}/pause

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Responses

  • 200 Survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/surveys/exp_123/pause \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Start survey

/surveys/{id}/start

Requires the Surveys lab for the shop; shops without it receive 404 Not Found.

Parameters

  • id in path required

Responses

  • 200 Survey
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/surveys/exp_123/start \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Preview brand completion undo

/api/multi-shop-programs/{id}/actions/undo-completion/preview

Not yet enabled: both routes return 404 while the server rollout is off. Dashboard session, required initiating shop pin, program access and per-store write/launch rights. Explicit all_with_source includes the source once; all means copies only; shop_id selects source or one copy. Preview is read-only. Apply binds actor, pin, policy, exact targets and program membership. Structural conflicts before execution write nothing. Later refusals produce partial receipts; each target commits independently. A same-key replay never repeats committed lifecycle work. Re-completion requires a new preview and key.

Parameters

  • id in path required
  • x-apex-shop in header required

Request body

{
  scope?: "all" | "all_with_source"
  shop_id?: string (uuid)
}

Responses

  • 200 Per-store eligibility and redacted no-access targets
  • 400 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 401 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 403 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 404 Not found, including while server rollout is off.
  • 409 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 500 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 503 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/api/multi-shop-programs/exp_123/actions/undo-completion/preview \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "scope": "all",
  "shop_id": "uuid-here"
}'
POST

Undo completion independently in each reviewed store

/api/multi-shop-programs/{id}/actions/undo-completion

Not yet enabled: both routes return 404 while the server rollout is off. Dashboard session, required initiating shop pin, program access and per-store write/launch rights. Explicit all_with_source includes the source once; all means copies only; shop_id selects source or one copy. Preview is read-only. Apply binds actor, pin, policy, exact targets and program membership. Structural conflicts before execution write nothing. Later refusals produce partial receipts; each target commits independently. A same-key replay never repeats committed lifecycle work. Re-completion requires a new preview and key.

Parameters

  • id in path required
  • x-apex-shop in header required

Request body

{
  scope?: "all" | "all_with_source"
  shop_id?: string (uuid)
  reason: string
  idempotency_key: string (uuid)
  preview_version: string
  targets: object[]
}

Responses

  • 200 Durable store receipts, including partial or fully blocked results
  • 400 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 401 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 403 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 404 Not found, including while server rollout is off.
  • 409 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 500 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
  • 503 Typed refusal. Errors after partial work carry committed:true and durable, access-redacted receipts.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/api/multi-shop-programs/exp_123/actions/undo-completion \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "example_reason",
  "idempotency_key": "uuid-here",
  "preview_version": "example_preview_version",
  "targets": []
}'
POST

Create a rule experience

/api/flags/{id}/experiences

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required

Request body

{
  anti_flicker_selectors?: string[]
  code_delivery_mode?: "inline" | "external"
  custom_css?: string | null
  custom_js?: string | null
  environment_key?: string
  expected_updated_at: string (date-time)
  key: string
  mutations?: any[]
  name: string
  reset_js?: string | null
  value?: any
}

Responses

  • 201 Updated flag
  • 400 Request refused
  • 401 Request refused
  • 403 Request refused
  • 404 Request refused
  • 409 Request refused
  • 422 Request refused
  • 429 Request refused
  • 502 Request refused
  • 503 Request refused
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/api/flags/exp_123/experiences \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_updated_at": "example_expected_updated_at",
  "key": "example_key",
  "name": "example_name"
}'
PATCH

Update a rule experience

/api/flags/{id}/experiences/{experienceId}

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required
  • experienceId in path required

Request body

{
  anti_flicker_selectors?: string[]
  code_delivery_mode?: "inline" | "external"
  custom_css?: string | null
  custom_js?: string | null
  environment_key?: string
  expected_updated_at: string (date-time)
  key?: string
  mutations?: any[]
  name?: string
  reset_js?: string | null
  value?: any
}

Responses

  • 200 Updated flag
  • 400 Request refused
  • 401 Request refused
  • 403 Request refused
  • 404 Request refused
  • 409 Request refused
  • 422 Request refused
  • 429 Request refused
  • 502 Request refused
  • 503 Request refused
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/api/flags/exp_123/experiences/experience_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_updated_at": "example_expected_updated_at"
}'
DELETE

Delete an unreferenced rule experience

/api/flags/{id}/experiences/{experienceId}

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required
  • experienceId in path required

Request body

{
  environment_key?: string
  expected_updated_at: string (date-time)
}

Responses

  • 200 Updated flag
  • 400 Request refused
  • 401 Request refused
  • 403 Request refused
  • 404 Request refused
  • 409 Request refused
  • 422 Request refused
  • 429 Request refused
  • 503 Request refused
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/api/flags/exp_123/experiences/experience_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "environment_key": "example_environment_key",
  "expected_updated_at": "example_expected_updated_at"
}'
PUT

Replace ordered flag audiences

/api/flags/{id}/rules

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required

Request body

{
  environment_key?: string
  expected_updated_at: string (date-time)
  rules: object[]
}

Responses

  • 200 Updated flag
  • 400 Request refused
  • 401 Request refused
  • 403 Request refused
  • 404 Request refused
  • 409 Request refused
  • 422 Request refused
  • 429 Request refused
  • 503 Request refused
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/api/flags/exp_123/rules \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "environment_key": "example_environment_key",
  "expected_updated_at": "example_expected_updated_at",
  "rules": []
}'
POST

Reorder flag audiences

/api/flags/{id}/rules/reorder

Uses the parent flag expected_updated_at. Stale versions and archived flags return 409. Rule removals retain history. Referenced experiences cannot be deleted. The response is the complete updated flag. Experience saves include code_save with artifact review and publication receipts. A 502 error.details receipt with saved=true means the write committed but publication failed; reload before retrying. Rule saves report publication_warning separately.

Parameters

  • id in path required

Request body

{
  environment_key?: string
  expected_updated_at: string (date-time)
  rules: object[]
}

Responses

  • 200 Updated flag
  • 400 Request refused
  • 401 Request refused
  • 403 Request refused
  • 404 Request refused
  • 409 Request refused
  • 422 Request refused
  • 429 Request refused
  • 503 Request refused
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/api/flags/exp_123/rules/reorder \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "environment_key": "example_environment_key",
  "expected_updated_at": "example_expected_updated_at",
  "rules": []
}'
POST

Undo completion in the same run

/api/experiments/{id}/undo-completion

Not yet enabled: both routes return 404 while the server rollout is off. Dashboard session only. Requires experiments:write and experiments:launch for running restoration. Preview never writes. Applies re-read policy and bind actor, shop, completion, definition, history and membership versions. Committed refresh failures remain HTTP 200; retry the same key for publication only. Queued retries and in-flight external sends may still deliver.

Parameters

  • id in path required
  • x-apex-shop in header required

Request body

{
  expected_assignment_epoch: integer
  preview_version: string
  completion_id: string (uuid)
  idempotency_key: string (uuid)
  reason: string
}

Responses

  • 200 Durable commit receipt or idempotent replay
  • 400 invalid_request / shop_pin_required
  • 401 unauthenticated
  • 403 no_permission / member_suspended
  • 404 experiment_not_found
  • 409 undo_window_expired / undo_disabled / serving_derivative_exists / configuration_changed / assignment_epoch_mismatch / scheduled_end_in_past / resume_blocked / experiment_changed / run_changed / preview_changed / policy_changed / program_deployment_moved / completion_scope_mismatch / completion_evidence_missing / completion_not_last_lifecycle_event / run_not_closed_by_completion / completion_timestamp_missing / status_not_completed / experiment_archived / program_archived / program_action_in_progress / action_in_progress / idempotency_key_mismatch
  • 503 policy_unavailable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/api/experiments/exp_123/undo-completion \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_assignment_epoch": 1,
  "preview_version": "example_preview_version",
  "completion_id": "uuid-here",
  "idempotency_key": "uuid-here",
  "reason": "example_reason"
}'
POST

Preview same-run completion undo

/api/experiments/{id}/undo-completion/preview

Not yet enabled: both routes return 404 while the server rollout is off. Dashboard session only. Requires experiments:write and experiments:launch for running restoration. Preview never writes. Applies re-read policy and bind actor, shop, completion, definition, history and membership versions. Committed refresh failures remain HTTP 200; retry the same key for publication only. Queued retries and in-flight external sends may still deliver.

Parameters

  • id in path required
  • x-apex-shop in header required

Request body

{
  expected_assignment_epoch: integer
}

Responses

  • 200 Eligible or blocked read-only preview
  • 400 invalid_request / shop_pin_required
  • 401 unauthenticated
  • 403 no_permission / member_suspended
  • 404 experiment_not_found
  • 409 undo_window_expired / undo_disabled / serving_derivative_exists / configuration_changed / assignment_epoch_mismatch / scheduled_end_in_past / resume_blocked / experiment_changed / run_changed / preview_changed / policy_changed / program_deployment_moved / completion_scope_mismatch / completion_evidence_missing / completion_not_last_lifecycle_event / run_not_closed_by_completion / completion_timestamp_missing / status_not_completed / experiment_archived / program_archived / program_action_in_progress / action_in_progress / idempotency_key_mismatch
  • 503 policy_unavailable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/api/experiments/exp_123/undo-completion/preview \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_assignment_epoch": 1
}'

Experiments

POST

Attach or upload QA evidence

/experiments/{id}/qa/evidence

Attaches a terminal screenshot job, writes typed evidence records, or uploads QA evidence images. Exactly one request form is accepted per call.

Request body

any

Responses

  • 200 Signed QA run with the updated evidence
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 502 Evidence upload failed
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/qa/evidence \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Create an artifact-injecting proxy preview session

/preview-sessions

Requires a secret API key with experiments:write. The returned short-lived URL is a bearer capability that renders the selected draft variation without a dashboard session or installed Apex snippet.

Request body

{
  experiment_id: string
  variation_id?: string
  target_url?: string (uri)
  ttl_hours?: integer = 168
}

Responses

  • 200 Preview session
  • 400
  • 401
  • 403
  • 404
  • 409 QA run changed during preview; retry
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/preview-sessions \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "experiment_id": "example_experiment_id",
  "variation_id": "example_variation_id",
  "target_url": "https://example.com",
  "ttl_hours": 168
}'
GET

List experiments

/experiments

Returns a paginated list of experiments. Supports cursor-based pagination and optional status filter.

Parameters

  • view in query - Full retains the existing response; summary omits code, targeting objects and goals unless fields requests a full-view field, which switches automatically to full details.
  • fields in query - Comma-separated top-level keys. Always keeps id. Fields outside summary switch automatically to full details. Unknown keys return 400 with allowed keys.
  • status in query
  • url in query - Absolute storefront URL; returns only experiments whose effective URL targeting matches it, evaluated server-side. Experiments with no URL rules match every URL.
  • archived in query - Set to 1 to include archived experiments
  • limit in query
  • cursor in query - ID of the last item from the previous page

Responses

  • 200 Paginated list of experiments
  • 400 invalid_view, invalid_fields, or invalid_status; the error lists allowed values.
  • 401 Unauthorized
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create experiment

/experiments

Creates a new experiment. Default control and variant are created if variations are not provided. Omitted statistical settings use the shop testing defaults. Experiments authenticated with a user API key default owner_user_id to that key's user; organization- and shop-scoped keys have no user and leave the owner null. When commerce sentinel mutations are supplied without goals, the shop default takes priority, followed by the oldest active unscoped revenue goal. The first attached goal is primary. For mutation drafts with URL or page-rule targeting, omitted or null store_url triggers an observed-page inventory lookup with a 1.5-second budget before the database transaction. A matching observed URL is saved only if it still matches the final persisted targeting. If no matching page is available, including on lookup timeout, store_url remains null and the partial draft is still created. An explicit store_url takes precedence. Redirect creation keeps its existing entry-URL behavior.

Request body

{
  name: string
  slug?: string | null
  description?: string | null
  backlog_item_id?: string (uuid)
  idea_key?: string
  as_follow_up?: boolean = false
  owner_user_id?: string (uuid) | null
  counting_type?: "standard" | "setup_test" | "aa_test" | "non_program" = "standard"
  delivery_type?: "mutation" | "redirect" = "mutation"
  type?: "standard" | "setup_test" | "aa_test" | "non_program"
  experiment_type?: "mutation" | "redirect"
  traffic_allocation?: number = 1
  experiment_js?: string | null
  experiment_reset_js?: string | null
  experiment_css?: string | null
  sequential_testing_enabled?: boolean
  significance_level?: number
  statistical_power?: number
  store_url?: string (uri) | null
  auto_stop_override?: AutoStopOverride
  guardian_override?: GuardianOverride
  targeting?: ExperimentTargeting | null
  variations?: object[]
  primary_goal_id?: string (uuid) | null
  goal_ids?: string (uuid)[]
  expectedCommerceRevision?: integer | null
  expectedArtifactHash?: string
  definition?: object
}

Responses

  • 200 Existing experiment already linked to the backlog item
  • 201 Experiment created
  • 400 Validation error
  • 401 Unauthorized
  • 404 Backlog item not found for this shop
  • 409 commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 Backlog item requires customer approval or the dashboard landing-page handoff, or both backlog_item_id and idea_key were sent commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
  • 429 Rate limit exceeded
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example_name"
}'
GET

Get experiment

/experiments/{id}

Returns a single experiment by ID with variations and goals.

Parameters

  • include in query - Pass preview_target to resolve and include the preview destination. Omitted by default to avoid an inventory lookup.
  • id in path required

Responses

  • 200 Experiment details
  • 404 Experiment not found
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update experiment

/experiments/{id}

Partially updates an experiment. Completed experiments accept only a window-only body (`results_weekdays`), which changes how the ended test is judged without editing the test. Audience and page targeting writes return 409 composition_locked while an experiment is running, completed or archived; the v1 API accepts other edits on draft experiments only, except a body that carries only `results_weekdays`, which it accepts on non-draft experiments. Unchanged launch-critical values preserve existing QA approval and scheduling. Material setup changes revoke launch readiness and write a same-transaction Activity audit with reason `launch_critical_edit`. If that required audit fails, the update fails and rolls back.

Parameters

  • id in path required

Request body

{
  name?: string
  slug?: string | null
  description?: string | null
  owner_user_id?: string (uuid) | null
  results_weekdays?: ResultsWeekdays | null
  counting_type?: "standard" | "setup_test" | "aa_test" | "non_program"
  type?: "standard" | "setup_test" | "aa_test" | "non_program"
  traffic_allocation?: number
  experiment_js?: string | null
  experiment_reset_js?: string | null
  experiment_css?: string | null
  sequential_testing_enabled?: boolean
  store_url?: string (uri) | null
  auto_stop_override?: AutoStopOverride
  guardian_override?: GuardianOverride
  targeting?: ExperimentTargeting | null
  confirmTargetingReplace?: boolean
}

Responses

  • 200 Experiment updated
  • 400 Cannot update completed experiment (a window-only results_weekdays body is the exception)
  • 404 Experiment not found
  • 409 Targeting replacement requires confirmation (error.code confirm_targeting_replace_required): the update would replace the experiment's author-explicit URL targeting with a page-entity rule. Retry with confirmTargetingReplace: true. Retired historical targeting writes return 409 checkout_mode_retired.
  • 422 Invalid experiment constraints.
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/experiments/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Archive experiment

/experiments/{id}

Archives an experiment while preserving its variations and results history.

Parameters

  • id in path required

Responses

  • 200 Experiment deleted
  • 404 Experiment not found
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/experiments/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read the experiment filesystem JSON Schema

/experiments/filesystem/schema

Returns the versioned JSON Schema document bundled by the Apex CLI.

Responses

  • 200 Experiment filesystem schema
  • 401 Unauthorized
  • 403 Forbidden
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/filesystem/schema \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read experiment filesystem

/experiments/{id}/filesystem

Returns the complete canonical filesystem snapshot for an experiment. Commerce drafts are represented as typed commerce/<kind>.json files with read-only serverOwned fields. commerce/discount-codes.json declares per-variation code blocking and optional en/de messages (1 to 120 characters); control never blocks. It combines with price, shipping and offer levers and requires the automatic offers discount owner at launch. File hashes can be used to detect changes before planning a write.

Parameters

  • id in path required

Responses

  • 200 Canonical experiment filesystem
  • 401 Unauthorized
  • 404 Experiment not found
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/filesystem \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Plan an experiment filesystem apply

/experiments/{id}/filesystem/plan

Validates a complete filesystem snapshot and reports the changes without writing them. Typed commerce files are draft-only and preserve server-owned carrier fields. Draft experiments accept all supported filesystem changes. Paused experiments accept treatment-content changes and redirect destinations. Running experiments accept treatment-content changes only; redirect destinations remain draft/paused-only. Structural changes remain draft-only and are reported with the offending file paths.

Parameters

  • id in path required

Request body

{
  files: object[]
}

Responses

  • 200 Filesystem apply plan
  • 400 Invalid request body
  • 401 Unauthorized
  • 404 Experiment not found
  • 429 Rate limit exceeded
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/filesystem/plan \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "files": []
}'
POST

Apply an experiment filesystem

/experiments/{id}/filesystem/apply

Atomically applies experiment, goal, variation, and variation-artifact changes to draft experiments. Paused experiments accept treatment-content changes and redirect destinations. Running experiments accept treatment-content changes only; redirect destinations remain draft/paused-only. Structural changes remain draft-only and fail closed with the offending file paths. Running artifact writes republish the storefront configuration. A successful response with applied=true means every database change committed. applied=false means no changes from this request committed, except when saved=true reports a committed artifact whose storefront publication failed. Identically re-applying an already committed snapshot is safe and converges on the same final state.

Parameters

  • id in path required

Request body

{
  accept_qa_invalidation?: boolean
  confirmTargetingReplace?: boolean
  files: object[]
}

Responses

  • 200 Atomic apply outcome
  • 400 Invalid request body
  • 401 Unauthorized
  • 404 Experiment not found
  • 409 Targeting replacement requires confirmation (error.code confirm_targeting_replace_required). Retry with confirmTargetingReplace: true. A fresh QA approval requires acknowledgement when accept_qa_invalidation is false (error.code qa_acknowledgment_required, error.details { qa_approved_at, required_acknowledgment }); retry with accept_qa_invalidation: true after reviewing the approval. No writes occur for either confirmation refusal. Retired historical targeting writes return 409 checkout_mode_retired.
  • 422 Invalid experiment constraints.
  • 429 Rate limit exceeded
  • 500 Unexpected failure; the atomic database transaction was rolled back and no filesystem changes committed.
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/filesystem/apply \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "accept_qa_invalidation": false,
  "confirmTargetingReplace": false,
  "files": []
}'
GET

Get experiment results

/experiments/{id}/results

Returns the canonical experiment results read model, including totals, arms, decision, metrics, timeseries, warnings, and delivery health.

Parameters

  • id in path required
  • currency in query - Optional ISO currency code for revenue diagnostics.
  • segment in query - Include one built-in audience dim:value (for example device:mobile). Include wins over exclude_segment. Unknown dimensions, invalid or unobserved values fall back to the unsliced read with segment_applied.applied false and an explicit reason. One model build per request.
  • exclude_segment in query - Exclude one audience dim:value; ignored when segment is also present.

Responses

  • 200 Experiment results
  • 401 Unauthorized
  • 403 API key is missing experiment read permission
  • 404 Experiment not found
  • 429 Rate limit exceeded
  • 503 The default snapshot or live results are still computing, or the database is temporarily busy.
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/results \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get experiment by slug

/experiments/by-slug/{slug}

Fetch a single experiment by slug (scoped to the API key's shop). Archived variations are omitted unless include_archived=true.

Parameters

  • include in query - Pass preview_target to resolve and include the preview destination. Omitted by default to avoid an inventory lookup.
  • include_archived in query - Pass true to include archived variations and their archived_at timestamps.
  • slug in path required

Responses

  • 200 Experiment details
  • 401 Unauthorized
  • 404 Experiment not found
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/by-slug/example_slug \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Upsert experiment by slug

/experiments/by-slug/{slug}

Idempotently create or update a draft experiment by slug. New experiments use the shop testing defaults for omitted statistical settings and default owner_user_id to the authenticated user for user API keys; organization- and shop-scoped keys leave the owner null. If an experiment exists with this slug and is not in draft status, or an incoming variation slug belongs to an archived variation, returns 409. Unchanged launch-critical values preserve existing QA approval and scheduling. Material setup changes revoke launch readiness and write a same-transaction Activity audit with reason `launch_critical_edit`. If that required audit fails, the update fails and rolls back. On creation, commerce sentinel mutations without goals use the shop default, then the oldest active unscoped revenue goal; existing drafts keep their saved goals. Existing draft-only and replacement semantics still apply: preservation depends on the resulting material values, not merely on omitted fields.

Parameters

  • slug in path required

Request body

{
  name: string
  description?: string | null
  counting_type?: "standard" | "setup_test" | "aa_test" | "non_program"
  delivery_type?: "mutation" | "redirect"
  type?: "standard" | "setup_test" | "aa_test" | "non_program"
  experiment_type?: "mutation" | "redirect"
  traffic_allocation?: number = 1
  experiment_js?: string | null
  experiment_reset_js?: string | null
  experiment_css?: string | null
  sequential_testing_enabled?: boolean
  significance_level?: number
  statistical_power?: number
  store_url?: string (uri) | null
  auto_stop_override?: AutoStopOverride
  targeting?: ExperimentTargeting | null
  variations: object[]
  primary_goal_id?: string (uuid) | null
  goal_ids?: string (uuid)[]
  expectedCommerceRevision?: integer | null
  expectedArtifactHash?: string
  definition?: object
}

Responses

  • 200 Experiment updated
  • 201 Experiment created
  • 400 Validation error
  • 401 Unauthorized
  • 409 Conflict (experiment not in draft status or slug already exists). Retired historical targeting writes return 409 checkout_mode_retired. commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 Invalid experiment constraints. commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
  • 429 Rate limit exceeded
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/experiments/by-slug/example_slug \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example_name",
  "variations": []
}'
POST

Attach an idea to an experiment

/experiments/{id}/idea

Links the experiment to one idea. Send exactly one of backlog_item_id or idea_key. Repeating the request returns the existing link with 200 and writes no audit entry.

Parameters

  • id in path required

Request body

{
  backlog_item_id?: string (uuid)
  idea_key?: string
  as_follow_up?: boolean = false
}

Responses

  • 200 The experiment was already linked to this idea
  • 201 Idea attached
  • 400 No identifier, or a field of the wrong type
  • 401 Unauthorized
  • 403
  • 404 Experiment or idea not found
  • 409 The experiment already carries another idea, the idea already has a primary test, or a follow-up was requested for an idea with no primary test. error.code names which one; the dashboard route reports the same codes.
  • 422 Both backlog_item_id and idea_key were sent
  • 429 Rate limit exceeded
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/idea \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "backlog_item_id": "uuid-here",
  "idea_key": "example_idea_key",
  "as_follow_up": false
}'
DELETE

Detach the linked idea from an experiment

/experiments/{id}/idea

Removes one idea link. Without an identifier the primary link goes first, then the oldest link.

Parameters

  • id in path required
  • backlog_item_id in query - Remove the link to this idea instead of the primary link.
  • idea_key in query - Shop backlog key alternative to backlog_item_id.

Responses

  • 200 Idea detached
  • 401 Unauthorized
  • 403
  • 404 Experiment or idea link not found
  • 422 Both backlog_item_id and idea_key were sent, or an identifier was repeated or blank
  • 429 Rate limit exceeded
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/experiments/exp_123/idea \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Pause experiment

/experiments/{id}/pause

Pauses a running experiment.

Parameters

  • id in path required
  • scope in query - Set to store to act only on this store; omitted acts on every store when this is a brand-test parent.

Responses

  • 200 Experiment paused
  • 400 Only running experiments can be paused
  • 404 Experiment not found
  • 502 Commerce configuration failed before commit, or the lifecycle change committed but storefront publication failed
  • 503 Publication lock contended; retry after the Retry-After interval
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/pause \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Complete experiment

/experiments/{id}/complete

Marks a running or paused experiment as completed. Sets ended_at timestamp.

Parameters

  • id in path required
  • scope in query - Set to store to act only on this store; omitted acts on every store when this is a brand-test parent.

Responses

  • 200 Experiment completed
  • 400 Only running or paused experiments can be completed
  • 404 Experiment not found
  • 502 Commerce configuration failed before commit, or the lifecycle change committed but storefront publication failed
  • 503 Publication lock contended; retry after the Retry-After interval
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/complete \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Duplicate experiment

/experiments/{id}/duplicate

Creates a faithful copy of the experiment in draft status with its configuration, variation artifacts, landing-page bindings, and goal associations. Review, scheduling, runtime, and result state do not carry over.

Parameters

  • id in path required

Request body

{
  expectedSourceCommerceRevision?: integer | null
  expectedSourceArtifactHashes?: object
}

Responses

  • 201 Duplicate experiment created: the serialized v1 experiment with canonical targeting.url and targeting.authorUrl rule arrays.
  • 400 A variation artifact could not be copied; no duplicate is created
  • 404 Experiment not found
  • 409 A concurrent write caused a temporary transaction conflict (error code tx_write_conflict); retry the request. commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/duplicate \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedSourceCommerceRevision": 1,
  "expectedSourceArtifactHashes": {}
}'
POST

Generate experiment with AI

/experiments/generate

Uses AI to generate an experiment configuration based on a store URL and prompt.

Request body

{
  store_url: string (uri)
  prompt: string
  primary_goal_id?: string (uuid) | null
  goal_ids?: string (uuid)[]
  traffic_allocation?: number = 1
  targeting?: ExperimentTargeting | null
  screenshot_devices?: string[]
  expectedCommerceRevision?: integer | null
  expectedArtifactHash?: string
  definition?: object
}

Responses

  • 201 AI-generated experiment created
  • 400 Validation error
  • 409 commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/generate \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "store_url": "https://example.com",
  "prompt": "example_prompt"
}'
GET

Replay experiment lifecycle events

/experiment-events

Returns sequenced events for the API key's shop. Positions are gap-tolerant opaque decimal cursors that strictly increase in exposure order. When no further event matches, next_after jumps to the newest committed position so filtered polls do not rescan.

Parameters

  • after in query
  • limit in query
  • types in query - Comma-separated event types

Responses

  • 200 An ordered page of experiment lifecycle events
cURL
curl \
  https://app.drip-apex.com/api/v1/experiment-events \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read the stored commerce definition

/experiments/{id}/commerce

Parameters

  • id in path required - Experiment UUID or public ID / test key

Responses

  • 200 Canonical definition or stored legacy envelope, with compile output
  • 401
  • 403
  • 404 Experiment not found in this API key's shop, or it has no commerce definition
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/commerce \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Compile and save the canonical commerce definition

/experiments/{id}/commerce

Parameters

  • id in path required - Experiment UUID or public ID / test key

Request body

{
  definition: CommerceDefinition
  expectedRevision: integer | null
}

Responses

  • 200 Definition saved
  • 400
  • 401
  • 403
  • 404 Experiment not found in this API key's shop
  • 409 commerce_definition_revision_conflict, commerce_definition_legacy_readonly, commerce_definition_running_price_locked, artifact_status_conflict (a completed test, or a running or paused shipping test), artifact_hash_conflict, experiment_archived, variation_archived, variation_not_found, or commerce_price_variant_relationship. Stale or changed generic save inputs, including missing, forged, or expired prepared evidence, yield commerce_definition_revision_conflict with error.details.retriable=true and no violations, whether or not a priceFloor exists. Carrier refusals list every blocking issue in error.details.blockingIssues. Floor-specific refusals use commerce_price_below_floor for evaluation or unavailable evidence: retriable true for unreadable costs when a margin is enforced or belowCost is block (even without a margin and under either missingCost policy), or solely unavailable target membership (truthful placeholder violations), false for any definitive violation including undefined known-cost margin at conservative bound zero. The shared refusal schema also permits the launch diagnostic for unreadable saved-definition JSON with the same code, empty violations and retriable true; this does not make malformed PUT bodies valid or identify an evaluated below-floor variant. Acknowledgement cannot bypass either refusal. Floor lives only in definition; defaults missingCost=block, belowCost=warn apply at evaluation when omitted, with no thresholds.
  • 422 Compile refusals: commerce_definition_feature_unavailable, commerce_definition_price_invalid, commerce_definition_price_variants_unresolved, commerce_definition_variations_mismatch, commerce_definition_shipping_scopes_invalid (one to ten disjoint scopes and at most one default), commerce_definition_shipping_market_unresolved (market must resolve completely to active region countries), commerce_definition_shipping_rules_invalid (shipping rules need strictly ascending subtotals and free shipping only as the last rule; up to five rules per scope). Numeric and GID variant aliases resolve to the same variant; duplicate canonical overrides or scope ids refuse with commerce_definition_price_invalid. Unknown override or scope variants refuse with commerce_definition_price_variants_unresolved naming the variant. Inactive variants are listed in serverOwned.excludedVariants and missing included products in serverOwned.excludedProducts with reason not_found. Selection resolution refuses empty_selection, invalid_product_id, invalid_collection_id, installation_unavailable, catalog_scopes_missing, catalog_page_read_failed, catalog_page_incomplete, variant_page_incomplete, collection_membership_unresolved, variant_relationships_unresolved, invalid_variant_money, currency_mismatch, selection_resolution_failed, or a selection that prices no variants. commerce_definition_feature_unavailable also refuses selections over the 100-variant cap until #9033. When the artifact gate refuses a derived carrier, error.code is its first blocking code and error.details.blockingIssues lists all blocking issues.
  • 429
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/experiments/exp_123/commerce \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedRevision": 1
}'
GET

Export effective selection prices as CSV

/experiments/{id}/commerce/price/csv

Parameters

  • id in path required

Responses

  • 200 JSON envelope with RFC 4180 CSV text, definition revision, and text/csv; charset=utf-8 contentType. Informational columns product_id,variant_id,sku,product_title,variant_title,base_price followed by editable price version slugs in versions.order. Read-only candidate price columns follow editable columns. Informational <slug>__floor_status columns follow all price columns in versions.order and are ignored on import. Read-only percentage price cells are blank without an authored price and make no selling-price claim. Exact amount-off and authored prices remain unchanged. Failed cost reads retain missing_cost diagnostics.
  • 401
  • 403
  • 404 Experiment or definition not found in this shop; commerce_price_table_unavailable when there is no sold-item pricing projection
  • 409 commerce_price_table_unavailable for legacy or unresolved definitions
  • 422 commerce_definition_price_invalid, commerce_definition_price_variants_unresolved
  • 429
  • 500 Unexpected server error
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/commerce/price/csv \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Import price overrides from CSV

/experiments/{id}/commerce/price/csv

Parameters

  • id in path required
  • If-Match in header required - Quoted expected definition revision

Responses

  • 200 Import result; unchanged definitions are not written
  • 400 commerce_price_csv_revision_invalid: If-Match is not a quoted positive integer
  • 401
  • 403
  • 404 Experiment or definition not found in this shop; commerce_price_table_unavailable when there is no sold-item pricing projection
  • 409 commerce_definition_revision_conflict, commerce_definition_legacy_readonly, commerce_definition_running_price_locked, artifact_status_conflict (a completed test, or a running or paused shipping test), artifact_hash_conflict, experiment_archived, variation_archived, variation_not_found, or commerce_price_variant_relationship. Stale or changed generic save inputs, including missing, forged, or expired prepared evidence, yield commerce_definition_revision_conflict with error.details.retriable=true and no violations, whether or not a priceFloor exists. Carrier refusals list every blocking issue in error.details.blockingIssues. Floor-specific refusals use commerce_price_below_floor for evaluation or unavailable evidence: retriable true for unreadable costs when a margin is enforced or belowCost is block (even without a margin and under either missingCost policy), or solely unavailable target membership (truthful placeholder violations), false for any definitive violation including undefined known-cost margin at conservative bound zero. The shared refusal schema also permits the launch diagnostic for unreadable saved-definition JSON with the same code, empty violations and retriable true; this does not make malformed PUT bodies valid or identify an evaluated below-floor variant. Acknowledgement cannot bypass either refusal. Floor lives only in definition; defaults missingCost=block, belowCost=warn apply at evaluation when omitted, with no thresholds.; commerce_price_table_unavailable
  • 413 commerce_price_csv_too_large: body exceeds 2 MB
  • 415 commerce_price_csv_content_type_invalid: Content-Type must be text/csv
  • 422 Compile refusals: commerce_definition_feature_unavailable, commerce_definition_price_invalid, commerce_definition_price_variants_unresolved, commerce_definition_variations_mismatch, commerce_definition_shipping_scopes_invalid (one to ten disjoint scopes and at most one default), commerce_definition_shipping_market_unresolved (market must resolve completely to active region countries), commerce_definition_shipping_rules_invalid (shipping rules need strictly ascending subtotals and free shipping only as the last rule; up to five rules per scope). Numeric and GID variant aliases resolve to the same variant; duplicate canonical overrides or scope ids refuse with commerce_definition_price_invalid. Unknown override or scope variants refuse with commerce_definition_price_variants_unresolved naming the variant. Inactive variants are listed in serverOwned.excludedVariants and missing included products in serverOwned.excludedProducts with reason not_found. Selection resolution refuses empty_selection, invalid_product_id, invalid_collection_id, installation_unavailable, catalog_scopes_missing, catalog_page_read_failed, catalog_page_incomplete, variant_page_incomplete, collection_membership_unresolved, variant_relationships_unresolved, invalid_variant_money, currency_mismatch, selection_resolution_failed, or a selection that prices no variants. commerce_definition_feature_unavailable also refuses selections over the 100-variant cap until #9033. When the artifact gate refuses a derived carrier, error.code is its first blocking code and error.details.blockingIssues lists all blocking issues.; commerce_price_csv_header_mismatch, commerce_price_csv_unknown_variant, commerce_price_csv_duplicate_variant, commerce_price_csv_invalid. Invalid money uses commerce_definition_price_invalid with error.details.line and column. Unknown variants carry line.
  • 428 commerce_price_csv_revision_required: If-Match is required
  • 429
  • 500 Unexpected server error
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/experiments/exp_123/commerce/price/csv \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read grouped effective price rows

/experiments/{id}/commerce/price/rows

Parameters

  • id in path required
  • group in query
  • limit in query - Number of groups
  • cursor in query - Opaque cursor from the preceding page

Responses

  • 200 Groups sorted by label then key. Collection grouping includes only selected collections. Read-only amount-off and offer cells include discount sources. Every evaluated cell includes floor diagnostics before pagination; failed cost reads retain exact prices and missing_cost reasons, without inventing verified costs.
  • 400 commerce_price_rows_query_invalid: invalid grouping, limit or cursor
  • 401
  • 403
  • 404 Experiment or definition not found in this shop; commerce_price_table_unavailable when there is no sold-item pricing projection
  • 409 commerce_price_table_unavailable for legacy or unresolved definitions
  • 422 commerce_definition_price_invalid, commerce_definition_price_variants_unresolved
  • 429
  • 500 Unexpected server error
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/commerce/price/rows \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read experiment launch readiness

/experiments/{id}/readiness

Read-only checklist and human launch handoff. Requires experiments:read on shop keys or shops:read on user keys. Does not create QA runs, approve QA, or launch. Screenshot URLs are signed from the latest persisted QA run. The checkout item reports checkout-message warnings and live confirmation for launchable or running checkout tests.

Parameters

  • id in path required - Experiment UUID or public ID
  • variation_id in query - Select a live variation by UUID, public ID or slug for the signed preview.
  • target_url in query - Signed preview destination on the experiment's allowed store origin.

Responses

  • 200 Readiness checklist
  • 401 Invalid or missing API key
  • 403 Read scope or shop access required
  • 404 Experiment not found in this shop
  • 429 Rate limit exceeded
  • 503 Rate-limit backend unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/readiness \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read return on ad spend per experiment variant

/experiments/{id}/results/ad-roas

Current-run Meta ad-arrived visitors and shop-currency matched revenue, with spend allocated by each ad-hour's share of all matching traffic. Variant spend reconciles within a test; overlapping tests are not additive. Revenue per ad visitor includes non-buyers as zeros, with Welch intervals and differences from original. ROAS has no significance claim and is never a decision basis. Missing FX, truncation and read failures never become measured zeros.

Parameters

  • id in path required

Responses

  • 200 Ad ROAS read-out or explicit unknown state
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/results/ad-roas \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read experiment money by arrival channel

/experiments/{id}/results/by-channel

Current-run snapshot through as_of, pinned to a published copy generation. Identified money uses all exposed visitors, including non-buyers as zeros. Matched money plus identified unmatched and identity-free residuals equals the headline recomputed through that cutoff. Unavailable and degraded never carry zero-filled money. Channel comparisons suggest follow-up tests, never winners. Unknown includes missing consent and pre-tracking exposures. Updates hourly.

Parameters

  • id in path required

Responses

  • 200 Snapshot or explicit unavailable/degraded state
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/results/by-channel \
  -H "Authorization: Bearer <apex-secret-key>"

Admin

POST

Create a personal managed-client join link

/admin/client-links

Requires a user API key with admin:write. Uses the same validation, sellable-plan, consultant, referral and conflict rules as Admin > Links. It creates the link but does not send email.

Request body

{
  kind: "invite"
  plan: "self_managed" | "apex_managed" | "drip_managed"
  client_name: string
  client_type: "company" | "agency"
  email: string (email)
  consultant_user_id?: string (uuid) | null
  consultant_title?: string | null
  payment?: "card" | "invoice"
  discount_pct?: integer | null
  discount_months?: integer | null
  free_days?: integer
  store_url?: string (uri) | null
  expires_at?: string | null
  internal_note?: string | null
  teammates?: object[]
}

Responses

  • 201 Created link and join URL
  • 400
  • 401
  • 403
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/admin/client-links \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "kind": "invite",
  "plan": "self_managed",
  "client_name": "example_client_name",
  "client_type": "company",
  "email": "example_email"
}'
POST

Create an engineering account

/admin/customers

Requires a user API key with admin:write and a live platform role with onboarding:write. Customer accounts are refused with use_client_link; create them with POST /api/v1/admin/client-links (apex admin clients create) or Admin, Links. A claimed store also requires admin:write. Every creation records the reason in the audit.

Request body

{
  name: string
  type: "company" | "agency"
  category: "internal" | "demo" | "dogfood"
  plan: "self_managed" | "apex_managed" | "drip_managed"
  store?: any
  ownerEmail?: string (email)
  teammates: object[]
  consultant?: any
  reason: string
}

Responses

  • 201 Created, including any invitation delivery failures
  • 400 Invalid request, consultant_required or use_client_link
  • 401 Missing, expired or revoked user API key
  • 402 First store requires checkout; no account retained
  • 403 Missing key scope or live platform permission
  • 409 Name, domain, store claim or owner conflict
  • 429 Rate limit exceeded
  • 503 Audit, provisioning or owner identity verification unavailable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/admin/customers \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example_name",
  "type": "company",
  "category": "internal",
  "plan": "self_managed",
  "teammates": [],
  "reason": "example_reason"
}'
PATCH

Change an account category, managing agency or workspace type

/admin/customers/{orgId}

Requires a user API key with admin:write. Moves an existing organization between the customer, internal, demo and dogfood categories through the audited customer change path; the reason is recorded in the audit. Managing agency and workspace type changes require platform admin access. Only active, unmanaged agency organizations can manage a customer; self-management and chains are refused. Workspace type changes refuse archived organizations and unchanged values; becoming an agency requires no active stores or managing agency, and becoming a company requires unlinking every managed organization. A competing customer change answers 409.

Request body

any

Responses

  • 200 Category, managing agency or workspace type changed
  • 400 Invalid organization id, category, workspace type or reason; a managed organization cannot become an agency (managed)
  • 401 Missing, expired or revoked user API key
  • 403 Missing key scope or live platform permission
  • 404 Customer not found
  • 409 Customer changed before the write; refresh and retry. Workspace type conflicts: unchanged, stores_present, manages_organizations or archived
  • 429 Rate limit exceeded
  • 503 Audit could not be recorded
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/admin/customers/org_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Preview or move a set of stores to a customer

/admin/customers/{orgId}/move-stores

Requires admin:write on a user API key and the live apex-platform-admin role. dry_run returns the before/after access plan without moving stores or writing move audits. Confirmation uses the audited store-set chokepoint; keep_grants preserves explicit roles and overrides.

Request body

{
  shop_ids: string (uuid)[]
  reason: string
  keep_grants?: boolean = false
  dry_run?: boolean = false
}

Responses

  • 200 Plan or completed move: shops, brands, scope counts, programs, entitlement change, kept/removed grants and auditIds
  • 400 Invalid store ids or reason
  • 401 Invalid user API key
  • 403 Platform admin required
  • 404 Store or destination not found
  • 409 Brand split or concurrent move conflict
  • 429 Rate limit exceeded
  • 503 Audit unavailable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/admin/customers/org_123/move-stores \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_ids": [],
  "reason": "example_reason",
  "keep_grants": false,
  "dry_run": false
}'
GET

Read a customer's setup checklist

/admin/customers/{orgId}/setup

Requires admin:read on a user API key. Each item says whether it is done, open, waiting on the client, optional or unknown, and names its fix. A Research Hub read failure is reported in degraded, never as done (#9232).

Responses

  • 200 Setup checklist
  • 400 Invalid customer id
  • 401 Invalid user API key
  • 403 admin:read required
  • 404 Customer not found
  • 429 Rate limit exceeded
  • 503 Database busy
cURL
curl \
  https://app.drip-apex.com/api/v1/admin/customers/org_123/setup \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Apply one setup checklist fix

/admin/customers/{orgId}/setup

Requires admin:write on a user API key. Runs the existing audited writer for the fix with that writer's own permission check, then returns the fresh checklist (#9232).

Request body

{
  fix: "rename" | "set_plan" | "set_managed_by" | "set_consultant" | "enable_switch" | "resend_owner_invite" | "invite_owner" | "retry_teammates" | "link_research_hub" | "apply_plan_switches"
  shopId?: string (uuid)
  input?: any
  reason: string
}

Responses

  • 200 Updated setup checklist
  • 400 Invalid fix, input or reason
  • 401 Invalid user API key
  • 403 Permission for this fix required
  • 404 Customer, store or invitation not found
  • 409 The fix conflicts with the current state (owner_exists, owner_invitation_pending, research_hub_already_linked, research_hub_link_changed)
  • 429 Rate limit exceeded
  • 503 Audit unavailable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/admin/customers/org_123/setup \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "fix": "rename",
  "shopId": "uuid-here",
  "reason": "example_reason"
}'
GET

Read an organization’s monthly unique visitors

/admin/customers/{orgId}/unique-visitors

Each consented web visitor ID that loads at least one page with an active Apex script counts once per shop and calendar month in the shop’s analytics timezone (report timezone, then Shopify installation timezone, then UTC); detected bots, crawlers, preview, QA, non-production traffic and anonymous sentinel identities are excluded. Platform user key with admin:read scope and platform billing:read permission required. Includes archived shops, ordered by name. Failed shop reads have null counts and a null organization total. A month that has not started or is past the 365-day retention answers 400; a shop whose own timezone has not reached the month reads month_not_counted.

Parameters

  • orgId in path required
  • month in query required

Responses

  • 200 All organization shops for one month
  • 400 Invalid month or range
  • 401
  • 403
  • 404 Organization not found
  • 429
  • 503 Analytics or shop timezone temporarily unavailable; never reported as zero
cURL
curl \
  https://app.drip-apex.com/api/v1/admin/customers/org_123/unique-visitors \
  -H "Authorization: Bearer <apex-secret-key>"

Platform admin

POST

Archive a shop

/admin/shops/{id}/archive

Requires an admin:write user API key and refuses shops with running, non-archived experiments.

Responses

  • 200 Shop archived and delivery disabled
  • 401
  • 403
  • 404 Shop not found (pass-through orchestration body)
  • 409 Shop has running, non-archived experiments
  • 429
  • 503 Archival persisted but snippet republication pending, or publication lock contention — retriable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/admin/shops/exp_123/archive \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read every registered per-shop setting

/admin/shops/{id}/settings

Requires an admin:read platform user API key. Exact domain matching is case-insensitive; archived shops remain resolvable. Returns registry metadata and nullable override state for every registered key.

Responses

  • 200 Shop identity and registered settings
  • 401
  • 403
  • 404 Shop not found
  • 409 Ambiguous domain; pass a shop UUID
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/admin/shops/exp_123/settings \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Write a registered per-shop setting

/admin/shops/{id}/settings

Requires an admin:write platform user API key plus the registry's per-key adminWrite permission. Uses the audited shop-settings writer; keys owned by other write paths are refused. Resolve a domain with GET first, then pin the resolved UUID with x-apex-shop. Does not return a settings snapshot.

Parameters

  • x-apex-shop in header required - Must equal the resolved shop UUID, including when id is a domain

Request body

{
  key: string
  value: boolean | string | number | null
  reason?: string
  expectedVersion?: string | null
}

Responses

  • 200 Audited write persisted; precompile reports publication outcome when applicable
  • 400 Invalid payload or missing/mismatched x-apex-shop pin; validation issues appear at error.details.issues
  • 401
  • 403 Platform permission or registry per-key write permission refused
  • 404 Shop not found
  • 409 Stale expectedVersion or ambiguous domain (error.details.candidates lists shop UUIDs); use a shop UUID
  • 429
  • 503 Required audit could not be recorded or database temporarily unavailable
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/admin/shops/exp_123/settings \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "key": "example_key",
  "value": false,
  "reason": "example_reason",
  "expectedVersion": "example_expectedVersion"
}'
POST

Restore an archived shop

/admin/shops/{id}/restore

Requires an admin:write user API key. Delivery remains controlled by the shop runtime kill switch.

Responses

  • 200 Shop restored
  • 401
  • 403
  • 404 Shop not found (pass-through orchestration body)
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/admin/shops/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List tracking quarantine events

/admin/tracking/quarantine

Read-only platform view requiring a live PlatformAccess row.

Parameters

  • shopId in query
  • reasonCode in query
  • verdict in query
  • limit in query

Responses

  • 200 Quarantine rows
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/admin/tracking/quarantine \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read staff tracking health

/admin/shops/{id}/tracking-health

Returns tracking health for one shop with staff-only causes for unlinked orders.

Responses

  • 200 Staff per-shop tracking health
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/admin/shops/exp_123/tracking-health \
  -H "Authorization: Bearer <apex-secret-key>"

Account

GET

Get the current user's profile

/account/profile

Responses

  • 200 Account profile
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/account/profile \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update the current user's profile

/account/profile

Request body

{
  displayName?: string
  avatarDataUrl?: string | null
  themePreference?: "system" | "dark" | "light"
}

Responses

  • 200 Updated account profile
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/account/profile \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "displayName": "example_displayName",
  "avatarDataUrl": "example_avatarDataUrl",
  "themePreference": "system"
}'

Keys

GET

Read the presented key's shop identity

/key

Any valid shop key can read its own identity without a resource scope. Shop context headers and query parameters do not change its shop. User keys return kind user and shop_id null without resolving shop context. MCP user credentials continue to resolve their bound shop through /keys/self/shop.

Responses

  • 200 Key identity
  • 401
  • 403 Key or shop access is denied
  • 429
  • 503 Rate limiting backend unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/key \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read the connector's bound shop

/keys/self/shop

User keys only, shops:write. Returns the existing bound shop's domain and platform without requiring shops:read. Organization and live shop authorization are checked; unbound keys return 400.

Responses

  • 200 Connector's bound shop
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/keys/self/shop \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Bind the connector to a shop

/keys/self/shop

User keys only, shops:write. Bind an unbound key once to an authorized shop in its organization; a concurrent or subsequent binding returns 409. Binding and required audit are atomic.

Request body

{
  shop_id: string (uuid)
}

Responses

  • 200 Connector bound
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/keys/self/shop \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_id": "uuid-here"
}'

API keys

GET

List a shop's API keys

/keys

Parameters

  • shop_id in query required

Responses

  • 200 API key metadata; hashes and secrets are never included
  • 400
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/keys \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create a shop API key

/keys

Request body

{
  shop_id: string (uuid)
  name: string
  type: "live_secret" | "test_secret" | "publishable"
  permissions?: string[]
  rate_limit?: integer
  expires_at?: string (date-time) | null
}

Responses

  • 201 Created key, including its secret exactly once
  • 400
  • 403
  • 404
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/keys \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_id": "uuid-here",
  "name": "example_name",
  "type": "live_secret"
}'
DELETE

Revoke a shop API key

/keys/{id}

Parameters

  • id in path required
  • shop_id in query required

Responses

  • 200 Revoked
  • 400
  • 403
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/keys/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"

External tracking

GET

Read cross-domain identity handover settings

/external-tracking

Responses

  • 200 External tracking settings
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/external-tracking \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update cross-domain identity handover settings

/external-tracking

Request body

{
  externalTracking?: object
  ifRevision?: string (date-time) | null
}

Responses

  • 200 Updated handover settings and edge precompile outcome
  • 400
  • 401
  • 403
  • 409
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/external-tracking \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "externalTracking": {},
  "ifRevision": "example_ifRevision"
}'

Variations

POST

Generate variation with AI

/experiments/{id}/variations/generate

Uses AI to generate a new variation for an existing experiment.

Parameters

  • id in path required - Experiment ID

Request body

{
  prompt: string
  expectedCommerceRevision?: integer | null
  expectedArtifactHash?: string
  definition?: object
}

Responses

  • 201 AI-generated variation created
  • 400 Validation error
  • 404 Experiment not found
  • 409 commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/variations/generate \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "prompt": "example_prompt",
  "expectedCommerceRevision": 1,
  "expectedArtifactHash": "example_expectedArtifactHash",
  "definition": {}
}'
POST

Create variation

/variations

Creates a new variation for a draft experiment. Requires experiment_id in the body.

Request body

{
  experiment_id: string (uuid) | string
  name: string
  weight: number
  is_control?: boolean = false
  redirect_url?: string (uri) | null
  mutations?: Mutation[]
  url_blocks?: UrlBlocks
  custom_css?: string | null
  custom_js?: string | null
  expectedCommerceRevision?: integer | null
}

Responses

  • 201 Variation created
  • 400 Validation error or experiment not in draft status
  • 404 Experiment not found
  • 409 commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/variations \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example_name",
  "weight": 1
}'
GET

Get variation

/variations/{id}

Returns a single variation by ID.

Parameters

  • id in path required

Responses

  • 200 Variation details
  • 403 Not authorized to access this variation
  • 404 Variation not found
cURL
curl \
  https://app.drip-apex.com/api/v1/variations/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update variation

/variations/{id}

Partially updates a variation. Treatment artifacts (mutations, URL blocks, and custom CSS/JavaScript) can change while the experiment is draft, paused, or running. Running artifact writes republish the storefront configuration. Other variation fields remain draft-only. Cannot update is_control.

Parameters

  • id in path required

Request body

{
  name?: string
  weight?: number
  redirect_url?: string (uri) | null
  expected_prior_redirect_url?: string (uri) | null
  mutations?: Mutation[]
  url_blocks?: UrlBlocks
  custom_css?: string | null
  custom_js?: string | null
  expectedCommerceRevision?: integer | null
  expectedArtifactHash?: string
  definition?: object
}

Responses

  • 200 Variation updated
  • 400 The write was rejected, or treatment artifacts committed before a later publication step reported a failure
  • 403 Not authorized
  • 404 Variation not found
  • 409 Treatment artifacts cannot change after the experiment finishes, the redirect destination changed since it was read, the experiment is no longer editable, or a price-test mutation targets a subscription-priced or bundle-component variant commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
  • 502 The running treatment artifact was saved, but storefront publication failed
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/variations/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Archive variation

/variations/{id}

Archives a non-control variation from a draft experiment. Minimum 2 active variations must remain, and remaining active weights are redistributed.

Parameters

  • id in path required

Request body

{
  expectedCommerceRevision?: integer | null
}

Responses

  • 200 Variation archived
  • 400 Cannot delete control, non-draft experiment, or last two variations
  • 403 Not authorized
  • 404 Variation not found
  • 409 commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/variations/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedCommerceRevision": 1
}'
POST

Restore variation

/variations/{id}/restore

Restores an archived variation and redistributes all active variation weights.

Parameters

  • id in path required

Request body

{
  expectedCommerceRevision?: integer | null
}

Responses

  • 200 Variation restored
  • 404 Variation not found
  • 409 Variation is already active or changed concurrently commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/variations/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedCommerceRevision": 1
}'

Apex Workspace

GET

Read variation workspace

/experiments/{id}/variations/{variationId}/workspace

Returns the canonical Apex workspace for an exact experiment variation. Used by `apex workspace checkout`.

Parameters

  • id in path required - Experiment ID
  • variationId in path required - Variation ID

Responses

  • 200 Canonical workspace payload
  • 401 Unauthorized
  • 403 Not authorized to access this variation
  • 404 Experiment variation workspace not found
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/variations/variation_123/workspace \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List variation workspace files

/experiments/{id}/variations/{variationId}/files

Lists canonical workspace files for an exact experiment variation.

Parameters

  • id in path required
  • variationId in path required

Responses

  • 200 Workspace files
  • 401 Unauthorized
  • 403 Not authorized to access this variation
  • 404 Experiment variation workspace not found
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/variations/variation_123/files \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read variation workspace file

/experiments/{id}/variations/{variationId}/files/{path}

Reads one canonical workspace file. URL-encode the file path, for example src%2Findex.js.

Parameters

  • id in path required
  • variationId in path required
  • path in path required - Canonical workspace path, URL-encoded. Supported roots: src, goals, segments, network, metrics.json, targeting.json, experiment.config.json, dev-doc.md.

Responses

  • 200 Workspace file
  • 401 Unauthorized
  • 403 Not authorized to access this variation
  • 404 Workspace file not found
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/variations/variation_123/files/example_path \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Write variation workspace file

/experiments/{id}/variations/{variationId}/files/{path}

Writes one canonical workspace file and records a workspace revision. Supply expected_version from the current file sha256 to reject a concurrent edit.

Parameters

  • id in path required
  • variationId in path required
  • path in path required - Canonical workspace path, URL-encoded. Supported roots: src, goals, segments, network, metrics.json, targeting.json, experiment.config.json, dev-doc.md.

Request body

{
  content: string
  source?: "manual" | "operator" | "build" | "system" = "operator"
  title?: string
  expected_version?: string
}

Responses

  • 200 Workspace file write result
  • 400 Validation error or unsupported workspace path
  • 401 Unauthorized
  • 403 Not authorized to edit this variation
  • 404 Experiment variation workspace not found
  • 409 The workspace file changed or no longer exists
  • 429 Rate limit exceeded
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/experiments/exp_123/variations/variation_123/files/example_path \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "content": "example_content",
  "source": "manual",
  "title": "example_title",
  "expected_version": "example_expected_version"
}'
POST

Build variation workspace

/experiments/{id}/variations/{variationId}/build

Projects canonical workspace files into Apex runtime state: mutations, CSS, JS, targeting, goals, and segments.

Parameters

  • id in path required
  • variationId in path required

Request body

{
  mode?: "full" | "validate_only" = "full"
  allowGoalWrites?: boolean = false
  expectedCommerceRevision?: integer | null
  expectedArtifactHash?: string
  definition?: object
  expectedWorkspaceRevision?: string
  expectedFileVersions?: object
}

Responses

  • 200 Workspace build succeeded
  • 400 Workspace build failed
  • 401 Unauthorized
  • 403 Not authorized to edit this variation
  • 404 Experiment variation workspace not found
  • 409 Workspace build conflicted with a concurrent artifact save commerce_definition_revision_conflict, artifact_hash_conflict, commerce_definition_legacy_readonly, or workspace/file conflict. No automatic retry with fresh tokens.
  • 422 commerce_definition_feature_unavailable with escaped paths; no partial authoring writes.
  • 429 Rate limit exceeded
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/variations/variation_123/build \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

List variation workspace revisions

/experiments/{id}/variations/{variationId}/revisions

Lists recent workspace revisions for an exact experiment variation.

Parameters

  • id in path required
  • variationId in path required

Responses

  • 200 Workspace revisions
  • 401 Unauthorized
  • 403 Not authorized to access this variation
  • 404 Experiment variation workspace not found
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/experiments/exp_123/variations/variation_123/revisions \
  -H "Authorization: Bearer <apex-secret-key>"

Targeting

GET

Test URL targeting

/targeting/test

Evaluates the API key shop's experiment targeting rules against one URL and returns all matching experiments for parallel-experiment awareness.

Parameters

  • url in query required
  • status in query - Comma-separated statuses or all. Defaults to draft,qa,running,paused.

Responses

  • 200 Targeting evaluation
  • 400 Invalid URL
  • 401 Unauthorized
  • 403 API key is missing experiment read permission
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/targeting/test \
  -H "Authorization: Bearer <apex-secret-key>"

Harness

POST

Publish harness session

/harness/sessions/{id}/publish

Publishes reviewed harness artifacts to the session variation.

Parameters

  • id in path required

Responses

  • 200 Harness session published
  • 400 Missing JavaScript artifact or artifact review blocked publish
  • 404 Harness session not found
  • 409 Artifact conflict or experiment is not in draft status
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/harness/sessions/exp_123/publish \
  -H "Authorization: Bearer <apex-secret-key>"

Screenshots

POST

Create screenshot job

/experiments/{id}/screenshots

Queues a screenshot capture job for the specified variations and devices.

Parameters

  • id in path required - Experiment ID

Request body

{
  devices: string[]
  variation_ids: (string (uuid) | string)[]
}

Responses

  • 202 Screenshot job queued
  • 400 Validation error
  • 404 Experiment not found
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/experiments/exp_123/screenshots \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "devices": [],
  "variation_ids": []
}'
GET

Get screenshot job status

/screenshots/{jobId}

Returns the current status and results of a screenshot job.

Parameters

  • jobId in path required

Responses

  • 200 Screenshot job details
  • 404 Job not found
cURL
curl \
  https://app.drip-apex.com/api/v1/screenshots/job_123 \
  -H "Authorization: Bearer <apex-secret-key>"

Webhooks

GET

List webhooks

/webhooks

Returns all webhooks for the shop.

Parameters

  • include_archived in query - Include archived webhooks.

Responses

  • 200 List of webhooks
  • 401 Unauthorized
cURL
curl \
  https://app.drip-apex.com/api/v1/webhooks \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create webhook

/webhooks

Registers a new webhook endpoint. URL must use HTTPS. Events include experiment.created, experiment.status_changed, experiment.dates_changed, experiment.targeting_changed, experiment.decided, experiment.results_snapshot, experiment.qa_signed_off, experiment.client_approval, experiment.ticket_linked, experiment.design_finished, experiment.plan_linked, experiment.archived, and the existing lifecycle and project events.

Request body

{
  url: string (uri)
  events: ("experiment.created" | "experiment.status_changed" | "experiment.dates_changed" | "experiment.targeting_changed" | "experiment.decided" | "experiment.results_snapshot" | "experiment.qa_signed_off" | "experiment.client_approval" | "experiment.ticket_linked" | "experiment.design_finished" | "experiment.plan_linked" | "experiment.archived" | "experiment.started" | "experiment.completed" | "experiment.significant" | "experiment.guardian_triggered" | "experiment.guardian_paused" | "variation.winning" | "screenshots.completed" | "project.published")[]
}

Responses

  • 201 Webhook created
  • 400 Validation error
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/webhooks \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com",
  "events": []
}'
GET

Get webhook

/webhooks/{id}

Parameters

  • id in path required

Responses

  • 200 Webhook
  • 404 Webhook not found
cURL
curl \
  https://app.drip-apex.com/api/v1/webhooks/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update webhook

/webhooks/{id}

Parameters

  • id in path required

Request body

{
  url?: string (uri)
  events?: ("experiment.created" | "experiment.status_changed" | "experiment.dates_changed" | "experiment.targeting_changed" | "experiment.decided" | "experiment.results_snapshot" | "experiment.qa_signed_off" | "experiment.client_approval" | "experiment.ticket_linked" | "experiment.design_finished" | "experiment.plan_linked" | "experiment.archived" | "experiment.started" | "experiment.completed" | "experiment.significant" | "experiment.guardian_triggered" | "experiment.guardian_paused" | "variation.winning" | "screenshots.completed" | "project.published")[]
  active?: boolean
}

Responses

  • 200 Webhook
  • 400 Invalid JSON, an unknown field, an unsupported event, or a URL that is malformed, not HTTPS, carries credentials or does not resolve
  • 403 The API key lacks webhooks:write, or the URL targets a local, private or reserved address
  • 404 Webhook not found
  • 409 Webhook is archived; restore it before editing
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/webhooks/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com",
  "events": [],
  "active": false
}'
DELETE

Archive webhook

/webhooks/{id}

Archives a webhook registration while retaining delivery history.

Parameters

  • id in path required

Responses

  • 200 Webhook deleted
  • 404 Webhook not found
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/webhooks/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Restore webhook

/webhooks/{id}/restore

Parameters

  • id in path required

Responses

  • 200 Restored webhook
  • 404 Webhook not found
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/webhooks/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List webhook deliveries

/webhooks/{id}/deliveries

Returns recent delivery attempts for one webhook. Delivery IDs are also sent as the X-Apex-Delivery-Id header on outbound webhook requests.

Parameters

  • id in path required
  • status in query
  • limit in query

Responses

  • 200 List of webhook delivery attempts
  • 404 Webhook not found
cURL
curl \
  https://app.drip-apex.com/api/v1/webhooks/exp_123/deliveries \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Send test webhook

/webhooks/{id}/test

Sends a signed test event and records the delivery.

Parameters

  • id in path required

Responses

  • 200 Test delivery. HTTP 200 reports that the attempt was recorded; delivery.status says whether the endpoint accepted it.
  • 404 Webhook not found
  • 409 Webhook is archived or inactive
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/webhooks/exp_123/test \
  -H "Authorization: Bearer <apex-secret-key>"

Organization

GET

Get the credential organization

/org

Responses

  • 200 Dashboard organization detail shape, including a `members` array (id, userId, email, role, defaultShopRole, createdAt), `_count.members`, and the organization policy booleans `mfa_enforced` (authenticator MFA required for members) and `ai_eu_only` (AI inference restricted to EU zero-retention endpoints). Both policies are read-only here and change only in the dashboard General Settings.
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/org \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update organization settings

/org

Request body

{
  name?: string
  slug?: string
  billing_email?: string | null
  logo_url?: string | null
  sso_enabled?: boolean
  sso_enforced?: boolean
  sso_domain?: string | null
  sso_provider_id?: string | null
}

Responses

  • 200 Updated organization
  • 401
  • 403
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/org \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

List organization members

/org/members

Responses

  • 200 Organization members, including suspension metadata
  • 400 Organization context required
  • 401
  • 403 Organization access denied or membership suspended
  • 404 Organization not found
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/org/members \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List organization invitations

/org/invitations

Responses

  • 200 Pending invitations
  • 401
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/org/invitations \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Invite an organization member

/org/invitations

Request body

{
  email: string (email)
  org_role?: "admin" | "member" = "member"
  default_shop_role?: "editor" = "editor"
}

Responses

  • 201 Invitation created
  • 401
  • 429
  • 502 The invitation email could not be delivered. The response message says whether the pending invitation was rolled back (safe to retry now) or is still present and blocking retries until it expires
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/org/invitations \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "example_email",
  "org_role": "admin",
  "default_shop_role": "editor"
}'
DELETE

Revoke an organization invitation

/org/invitations/{id}

Parameters

  • id in path required

Responses

  • 200 Invitation revoked
  • 401
  • 404
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/org/invitations/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update an organization member

/org/members/{id}

Parameters

  • id in path required

Request body

{
  role?: "admin" | "member"
  default_shop_role?: "editor"
  suspended?: boolean
  reason?: string
}

Responses

  • 200 Updated member, including nullable suspension metadata
  • 400 Invalid payload, self-suspension/reinstatement, or last active owner
  • 401
  • 403 Insufficient permissions or membership suspended
  • 404 Member not found
  • 429
  • 503 The database is briefly busy (code database_busy, retry after the Retry-After seconds), or the suspension or reinstatement was saved but the access-cache refresh failed (code authz_invalidation_failed, repeat the same request to finish the refresh)
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/org/members/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "role": "admin",
  "default_shop_role": "editor",
  "suspended": false,
  "reason": "example_reason"
}'
DELETE

Remove an organization member

/org/members/{id}

Parameters

  • id in path required

Responses

  • 200 Member removed
  • 401
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/org/members/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"

Team

GET

List shop team members

/team/members

Parameters

  • shop_id in query required

Responses

  • 200 Team members
  • 401
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/team/members \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List shop invitations

/team/invitations

Parameters

  • shop_id in query required

Responses

  • 200 Invitations
  • 401
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/team/invitations \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Invite a shop member

/team/invitations

Request body

{
  email: string (email)
  role: "editor" | "client_viewer"
  shop_id: string (uuid)
  permission_overrides?: object
}

Responses

  • 201 Invitation created
  • 400
  • 401
  • 403 The key's user lacks members:invite, or sent permission_overrides without members:manage
  • 409 An open invitation already exists for this address. code: role_mismatch_pending_invitation when its role differs from the requested role (cancel it first); code: invitation_conflict_retry when a concurrent change could not be resolved (retry).
  • 429
  • 502 The invitation email could not be delivered. The response message says whether the pending invitation was rolled back (safe to retry now) or is still present and blocking retries until it expires
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/team/invitations \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "example_email",
  "role": "editor",
  "shop_id": "uuid-here",
  "permission_overrides": {}
}'
DELETE

Revoke a shop invitation

/team/invitations/{id}

Parameters

  • id in path required

Request body

{
  shop_id: string (uuid)
}

Responses

  • 200 Invitation revoked
  • 401
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/team/invitations/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_id": "uuid-here"
}'
PATCH

Update a shop member

/team/members/{id}

Request body

{
  shop_id: string (uuid)
  role?: "editor" | "client_viewer"
  permission_overrides?: object
}

Responses

  • 200 Member updated
  • 401
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/team/members/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_id": "uuid-here",
  "role": "editor",
  "permission_overrides": {}
}'
DELETE

Remove a shop member

/team/members/{id}

Request body

{
  shop_id: string (uuid)
}

Responses

  • 200 Member removed
  • 401
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/team/members/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_id": "uuid-here"
}'

Shops

GET

List organization shops

/shops

Responses

  • 200 Shop list
cURL
curl \
  https://app.drip-apex.com/api/v1/shops \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create a shop

/shops

For owners with a completed or waived signup-offer redemption bound to an organization, shops are created in that billed organization regardless of the requested workspace target or organization.

Request body

{
  name: string
  domain: string
  platform: "shopify" | "hydrogen" | "shopware" | "woocommerce" | "centra" | "webflow" | "custom"
  plan?: "managed" | "self_serve" = "self_serve"
  origin?: string | null
  currency?: "AED" | "AFN" | "ALL" | "AMD" | "ANG" | "AOA" | "ARS" | "AUD" | "AWG" | "AZN" | "BAM" | "BBD" | "BDT" | "BGN" | "BHD" | "BIF" | "BMD" | "BND" | "BOB" | "BOV" | "BRL" | "BSD" | "BTN" | "BWP" | "BYN" | "BZD" | "CAD" | "CDF" | "CHE" | "CHF" | "CHW" | "CLF" | "CLP" | "CNY" | "COP" | "COU" | "CRC" | "CUP" | "CVE" | "CZK" | "DJF" | "DKK" | "DOP" | "DZD" | "EGP" | "ERN" | "ETB" | "EUR" | "FJD" | "FKP" | "GBP" | "GEL" | "GHS" | "GIP" | "GMD" | "GNF" | "GTQ" | "GYD" | "HKD" | "HNL" | "HRK" | "HTG" | "HUF" | "IDR" | "ILS" | "INR" | "IQD" | "IRR" | "ISK" | "JMD" | "JOD" | "JPY" | "KES" | "KGS" | "KHR" | "KMF" | "KPW" | "KRW" | "KWD" | "KYD" | "KZT" | "LAK" | "LBP" | "LKR" | "LRD" | "LSL" | "LYD" | "MAD" | "MDL" | "MGA" | "MKD" | "MMK" | "MNT" | "MOP" | "MRU" | "MUR" | "MVR" | "MWK" | "MXN" | "MXV" | "MYR" | "MZN" | "NAD" | "NGN" | "NIO" | "NOK" | "NPR" | "NZD" | "OMR" | "PAB" | "PEN" | "PGK" | "PHP" | "PKR" | "PLN" | "PYG" | "QAR" | "RON" | "RSD" | "RUB" | "RWF" | "SAR" | "SBD" | "SCR" | "SDG" | "SEK" | "SGD" | "SHP" | "SLE" | "SOS" | "SRD" | "SSP" | "STN" | "SVC" | "SYP" | "SZL" | "THB" | "TJS" | "TMT" | "TND" | "TOP" | "TRY" | "TTD" | "TWD" | "TZS" | "UAH" | "UGX" | "USD" | "USN" | "UYI" | "UYU" | "UYW" | "UZS" | "VES" | "VND" | "VUV" | "WST" | "XAF" | "XCD" | "XCG" | "XDR" | "XOF" | "XPF" | "XSU" | "YER" | "ZAR" | "ZMW" | "ZWG"
}

Responses

  • 201 Shop created
  • 402 The signup offer requires payment-method collection or is no longer available. Continue at /join.
  • 403
  • 409 The domain already has a live binding. Owner details are included only when the caller may see the owning shop.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example_name",
  "domain": "example_domain",
  "platform": "shopify"
}'
GET

Get a shop

/shops/{id}

Responses

  • 200 Shop detail
  • 404
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update a shop

/shops/{id}

Request body

{
  name?: string
  domain?: string
  origin?: string | null
  tier?: "self_managed" | "apex_managed" | "drip_managed" | "core" | "managed" | "enterprise"
  operator_enabled?: boolean
  holdout_pct?: number
  holdout_v1_enabled?: boolean
  currency?: "AED" | "AFN" | "ALL" | "AMD" | "ANG" | "AOA" | "ARS" | "AUD" | "AWG" | "AZN" | "BAM" | "BBD" | "BDT" | "BGN" | "BHD" | "BIF" | "BMD" | "BND" | "BOB" | "BOV" | "BRL" | "BSD" | "BTN" | "BWP" | "BYN" | "BZD" | "CAD" | "CDF" | "CHE" | "CHF" | "CHW" | "CLF" | "CLP" | "CNY" | "COP" | "COU" | "CRC" | "CUP" | "CVE" | "CZK" | "DJF" | "DKK" | "DOP" | "DZD" | "EGP" | "ERN" | "ETB" | "EUR" | "FJD" | "FKP" | "GBP" | "GEL" | "GHS" | "GIP" | "GMD" | "GNF" | "GTQ" | "GYD" | "HKD" | "HNL" | "HRK" | "HTG" | "HUF" | "IDR" | "ILS" | "INR" | "IQD" | "IRR" | "ISK" | "JMD" | "JOD" | "JPY" | "KES" | "KGS" | "KHR" | "KMF" | "KPW" | "KRW" | "KWD" | "KYD" | "KZT" | "LAK" | "LBP" | "LKR" | "LRD" | "LSL" | "LYD" | "MAD" | "MDL" | "MGA" | "MKD" | "MMK" | "MNT" | "MOP" | "MRU" | "MUR" | "MVR" | "MWK" | "MXN" | "MXV" | "MYR" | "MZN" | "NAD" | "NGN" | "NIO" | "NOK" | "NPR" | "NZD" | "OMR" | "PAB" | "PEN" | "PGK" | "PHP" | "PKR" | "PLN" | "PYG" | "QAR" | "RON" | "RSD" | "RUB" | "RWF" | "SAR" | "SBD" | "SCR" | "SDG" | "SEK" | "SGD" | "SHP" | "SLE" | "SOS" | "SRD" | "SSP" | "STN" | "SVC" | "SYP" | "SZL" | "THB" | "TJS" | "TMT" | "TND" | "TOP" | "TRY" | "TTD" | "TWD" | "TZS" | "UAH" | "UGX" | "USD" | "USN" | "UYI" | "UYU" | "UYW" | "UZS" | "VES" | "VND" | "VUV" | "WST" | "XAF" | "XCD" | "XCG" | "XDR" | "XOF" | "XPF" | "XSU" | "YER" | "ZAR" | "ZMW" | "ZWG"
}

Responses

  • 200 Shop updated
  • 403
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/shops/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
POST

Pre-bind a Shopware store to an Apex shop

/shops/{id}/shopware/prebind

Request body

{
  shopUrl: string (uri)
  environmentKey?: string = "production"
}

Responses

  • 200 Existing binding returned
  • 201 Pending binding created
  • 400
  • 401
  • 403
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/shopware/prebind \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shopUrl": "https://example.com",
  "environmentKey": "example_environmentKey"
}'
GET

Get auto-stop settings

/shops/{id}/auto-stop

Responses

  • 200 Settings
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/auto-stop \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update auto-stop settings

/shops/{id}/auto-stop

Request body

{
  enabled?: boolean
  mode?: "off" | "fixed_horizon_planned_end" | "sequential_significance"
  min_runtime_minutes?: integer
  sequential?: object
  fixed_horizon?: object
}

Responses

  • 200 Settings updated
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/shops/exp_123/auto-stop \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get testing-defaults settings

/shops/{id}/testing-defaults

Responses

  • 200 Settings
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/testing-defaults \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update testing-defaults settings

/shops/{id}/testing-defaults

Request body

{
  significanceLevel?: number
  statisticalPower?: number
  sequentialTestingEnabled?: boolean
  defaultPrimaryGoalId?: string (uuid) | null
}

Responses

  • 200 Settings updated
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/shops/exp_123/testing-defaults \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "significanceLevel": 1,
  "statisticalPower": 1,
  "sequentialTestingEnabled": false,
  "defaultPrimaryGoalId": "uuid-here"
}'
GET

Get guardian settings

/shops/{id}/guardian

Responses

  • 200 Settings
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/guardian \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update guardian settings

/shops/{id}/guardian

Request body

{
  enabled?: boolean
  pause_on_broken?: boolean
  pause_on_harm?: boolean
  alert_on_data_quality?: boolean
  delivery_drift?: object
  revenue_loss?: object | null
}

Responses

  • 200 Settings updated
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/shops/exp_123/guardian \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
GET

Get the store profile

/shops/{id}/store-profile

Responses

  • 200 The complete dashboard Store Profile contract. When the store-profile schema is not yet migrated, the route degrades to `{ schemaReady: false }` with HTTP 200 instead of failing.
  • 503 Store profile temporarily unavailable (database under pressure); retry shortly.
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/store-profile \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Review a store-profile fact

/shops/{id}/store-profile/facts/{factId}

Request body

{
  status: "suggested" | "confirmed" | "rejected" | "stale"
  claim?: string
  value?: any
}

Responses

  • 200 Fact reviewed
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/shops/exp_123/store-profile/facts/fact_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "suggested",
  "claim": "example_claim"
}'
POST

Review a semantic finding

/shops/{id}/store-profile/findings/review

Request body

{
  key: string
  claim: string
  status?: string
  title?: string
  category?: string
  source?: string
  confidence?: number
  value?: any
}

Responses

  • 200 Finding reviewed
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/store-profile/findings/review \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "key": "example_key",
  "claim": "example_claim"
}'
POST

Start a store-profile learning run

/shops/{id}/store-profile/learning-runs

Request body

{
  trigger?: string = "manual"
  sources?: string[]
  scope?: string[]
}

Responses

  • 201 Learning run queued
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/store-profile/learning-runs \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "trigger": "example_trigger",
  "sources": [],
  "scope": []
}'
POST

Issue a WooCommerce plugin install token

/shops/{id}/plugin-token

Requires shops:write. User API keys and OAuth bearer credentials also need the shop:settings role permission. Shop keys are limited to their own shop; bound OAuth/user keys cannot target another shop. Unbound user keys may target an accessible shop without X-Apex-Org. WooCommerce only. Rotates the same single-use token as the dashboard, expires in 48 hours, and replaces any unused token. Returns the configured plugin download URL and installation steps. Tokens are never included in audit logs. Launching tests is human-only.

Parameters

  • id in path required

Responses

  • 200 Single-use plugin token (Cache-Control: no-store)
  • 400
  • 401
  • 403
  • 404
  • 409 Store is not WooCommerce
  • 429
  • 503 Plugin download configuration or rate limiting backend unavailable
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/plugin-token \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get engine state and guardrails

/shops/{id}/engine

Parameters

  • id in path required

Responses

  • 200 Engine state
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get engine guardrails

/shops/{id}/engine/guardrails

Parameters

  • id in path required

Responses

  • 200 Engine guardrails
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/guardrails \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update engine guardrails with optimistic concurrency

/shops/{id}/engine/guardrails

Parameters

  • id in path required

Request body

{
  expected_version: integer
  allow?: object
  auto_launch?: object
  surfaces?: object
  segments?: any
  max_concurrent?: integer
  max_per_page?: integer
}

Responses

  • 200 Updated engine guardrails
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/guardrails \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_version": 1
}'
POST

Start engine

/shops/{id}/engine/start

Parameters

  • id in path required

Responses

  • 200 Engine state updated
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/start \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Pause engine

/shops/{id}/engine/pause

Parameters

  • id in path required

Responses

  • 200 Engine state updated
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/pause \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Stop engine

/shops/{id}/engine/stop

Parameters

  • id in path required

Responses

  • 200 Engine state updated
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/stop \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Approve engine proposal

/shops/{id}/engine/proposals/{pid}/approve

Parameters

  • id in path required
  • pid in path required

Responses

  • 200 Engine proposal approve decision recorded
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/proposals/p_123/approve \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Reject engine proposal

/shops/{id}/engine/proposals/{pid}/reject

Parameters

  • id in path required
  • pid in path required

Request body

{
  reason: string
}

Responses

  • 200 Engine proposal reject decision recorded
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/proposals/p_123/reject \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "example_reason"
}'
POST

Veto engine proposal

/shops/{id}/engine/proposals/{pid}/veto

Parameters

  • id in path required
  • pid in path required

Responses

  • 200 Engine proposal veto decision recorded
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/proposals/p_123/veto \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Roll a certified Engine winner out to 100% of traffic

/shops/{id}/engine/interventions/{iid}/rollout

Sends all eligible traffic to the winning variation, freezes the certified interval, and starts the decay watch. The program holdout is untouched.

Parameters

  • id in path required
  • iid in path required

Request body

{
  expected_updated_at: string (date-time)
}

Responses

  • 200 Engine intervention rolled out
  • 400
  • 401
  • 403
  • 404
  • 409 Rollout refused
  • 422
  • 429
  • 503 Either the certified measurement could not be read (rollout refused, retriable) or the rate-limiting backend is unavailable. Both are plain error envelopes; only the first carries a rollout `code`.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/shops/exp_123/engine/interventions/i_123/rollout \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_updated_at": "example_expected_updated_at"
}'
GET

Read shop installation status

/install-status

User keys need shops:read; shop keys need experiments:read. User keys use their bound shop, or select a shop they can access with X-Apex-Shop (or shop_id) when unbound; the organization follows from the shop, so account-scoped keys need no X-Apex-Org. Without any shop the route answers 400 with code shop_context_required (connectors call create_shop first). Uses the shared 60-minute customer telemetry window. Returns only shop_id, overall, last_event_time, connected, receivingData, optional domains, next_action and storefront_check (present only with steps=1&live=1). Unavailable telemetry is explicit and never a healthy default, and returns an empty domains list when per-domain detail is unavailable. With steps=1 it adds window_days, checked_at and steps: one row per install step (script, consent, environments, orders, qa_access) with state done, waiting, not_applicable or unavailable, first and last seen within the 7-day window, a detail sentence and evidence naming its source (events, page, firewall or none). live=1 also checks the storefront HTML and firewall access with the shop's verification header, sent only to the shop's own HTTPS hosts and never returned. Verified requires a visitor page view on the store's own address: not a bot, not QA, no QA session or live run, sent by the Apex script rather than the Shopify pixel, after the active install. A myshopify.com host counts only when it is the store's only domain; shopifypreview.com never counts. Theme editor or preview traffic leaves overall at partial. With steps=1 the script and consent steps follow the same visitor rule. live=1 also runs Apex's live storefront check and returns storefront_check: checking, on, not_saved, password, password_seen, challenge or unavailable, with the final URL and HTTP status.

Parameters

  • steps in query
  • live in query - With steps=1, also fetch the live storefront and test firewall access.

Responses

  • 200 Installation verification
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/install-status \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read shop installation instructions

/shops/{id}/install-instructions

User keys need shops:read; shop keys need experiments:read. Returns platform-specific plain instructions and the canonical snippet; Shopify OAuth is included only for shopify-platform shops. Bound keys may read their shop only; unbound user keys may read any shop they can access, and account-scoped keys need no X-Apex-Org.

Parameters

  • id in path required
  • consent_provider in query
  • consent_vendor_id in query
  • consent_setting_key in query
  • bot_protection in query - Comma-separated, case-insensitive: cloudflare, akamai, other. Empty items are ignored; any other value answers 400.
  • tools in query - Comma-separated testing tools already on the site, for example statsig,gempages.
  • ga4_purchase in query

Responses

  • 200 Installation instructions
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/install-instructions \
  -H "Authorization: Bearer <apex-secret-key>"

Audit logs

GET

List audit logs for an authorized shop with a user API key

/shops/{id}/audit-logs

Parameters

  • entity_type in query
  • entity_id in query
  • limit in query
  • offset in query

Responses

  • 200 Audit log page
  • 403
  • 404
cURL
curl \
  https://app.drip-apex.com/api/v1/shops/exp_123/audit-logs \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List audit logs

/audit-logs

Parameters

  • entity_type in query
  • entity_id in query
  • limit in query
  • offset in query

Responses

  • 200 Audit log page
  • 403
cURL
curl \
  https://app.drip-apex.com/api/v1/audit-logs \
  -H "Authorization: Bearer <apex-secret-key>"

Test results

GET

List past test results

/test-results

Read imported and native results for the key's shop. Requires experiments:read for shop and user keys. Results are ordered by created_at descending, then id descending.

Parameters

  • source in query
  • limit in query
  • cursor in query - Last row UUID from this shop. Invalid or foreign cursors return 400.

Responses

  • 200 Past test results
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/test-results \
  -H "Authorization: Bearer <apex-secret-key>"
DELETE

Remove an imported past test result

/test-results/{id}

Permanently remove one imported result from the key's shop. Requires experiments:write and confirm:true. The full record is retained in the audit log. Native results return 409; absent or foreign rows return 404; an unavailable required audit returns 500 without removing anything.

Parameters

  • id in path required - UUID, legacy ID or test key

Request body

{
  confirm: true
}

Responses

  • 200 Removed test result
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 500 Removal not recorded; nothing was removed
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/test-results/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "confirm": true
}'
GET

Get a past test result

/test-results/{id}

Read a result by UUID, case-sensitive legacy ID or test key for the key's shop. Requires experiments:read. Absent or foreign rows return 404.

Parameters

  • id in path required - UUID, legacy ID or test key

Responses

  • 200 Past test result
  • 400
  • 401
  • 403
  • 404
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/test-results/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Inspect structured test-result files

/test-results/import/inspect

Responses

  • 200 Import columns inspected
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/test-results/import/inspect \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Preview a structured test-result import

/test-results/import/preview

Responses

  • 200 Import preview
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/test-results/import/preview \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Confirm a test-result import

/test-results/import/confirm

Request body

{
  rows: object[]
}

Responses

  • 200 Imported test results
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/test-results/import/confirm \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "rows": []
}'

Analytics

GET

Show entry and exit page analytics

/analytics/entry-exit

Parameters

  • days in query

Responses

  • 200 Analytics data
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/entry-exit \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show geography and device analytics

/analytics/geo

Parameters

  • days in query

Responses

  • 200 Analytics data
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/geo \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Inspect retained order journey evidence

/analytics/order-journey

Secret or authorized user key only. Exactly one order identifier. Maximum 31 days; to cannot exceed server time by five minutes. Unknown and duplicate query fields are rejected. Read-only, private,no-store. Source failures return partial evidence with null counts. Eligibility is only for attributed_goal_counts, never final statistical contribution. Legacy consentedOrders/declinedOrders count identity presence, not historical consent. Retained processing receipts are compared per stage and assignment. Present goal-reader receipts identify one read-only snapshot and do not repair historical evidence. Goal-reader receipts exist only for orders inside the counted population; an excluded order reports its exclusion status without a result receipt. Transport success does not certify the integration.

Parameters

  • environment in query required
  • from in query required
  • include_qa in query
  • order_number in query
  • platform in query required
  • platform_order_id in query
  • to in query required

Responses

  • 200 Response
  • 400 Invalid bounded query
  • 401 Authentication required
  • 403 Secret analytics:read access required
  • 404 Shop not found
  • 429 Rate limited
  • 503 Integration profile unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/order-journey \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show page analytics

/analytics/pages

Parameters

  • days in query
  • planningDays in query - Planning window in days (planning_days is also accepted)
  • limit in query

Responses

  • 200 Analytics data
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/pages \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read store profit

/analytics/profit

Parameters

  • from in query required
  • to in query required

Responses

  • 200 Store profit
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/profit \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show realtime analytics

/analytics/realtime

Responses

  • 200 Analytics data
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/realtime \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show session analytics

/analytics/sessions

Parameters

  • days in query

Responses

  • 200 Analytics data
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/sessions \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show dashboard analytics stats

/analytics/stats

Parameters

  • days in query
  • shop_currency in query

Responses

  • 200 Analytics data
  • 400
  • 401
  • 403
  • 422
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/stats \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Suggest goals and funnels

/analytics/suggest-goals

Requires a secret API key with the analytics:read scope. The endpoint only reads analytics and returns suggestions — it creates no goals — but it spends model budget, so publishable keys receive 403 Forbidden and a per-shop 24-hour spend guard applies on top of the normal API rate limit.

Responses

  • 200 Suggested goals and funnels
  • 401
  • 403
  • 413 Request body is too large
  • 429
  • 502 The suggestion model returned unusable output
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/analytics/suggest-goals \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show analytics timeseries

/analytics/timeseries

Parameters

  • days in query

Responses

  • 200 Analytics data
  • 401
  • 403
  • 422
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/timeseries \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show traffic sources

/analytics/traffic

Parameters

  • days in query

Responses

  • 200 Analytics data
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/traffic \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show URL inventory

/analytics/url-inventory

Returns a shaped Tinybird pageview URL inventory for the API key's shop. Requires a secret API key with analytics:read.

Parameters

  • days in query
  • host in query
  • pathContains in query
  • limit in query
  • offset in query

Responses

  • 200 URL inventory
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/url-inventory \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Show daily visitor series

/analytics/visitor-series

Parameters

  • days in query

Responses

  • 200 Daily visitors and pageviews
  • 401
  • 403
  • 429
  • 503 Analytics is temporarily unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/analytics/visitor-series \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read monthly unique visitors for billing

/usage/unique-visitors

Each consented web visitor ID that loads at least one page with an active Apex script counts once per shop and calendar month in the shop’s analytics timezone (report timezone, then Shopify installation timezone, then UTC); detected bots, crawlers, preview, QA, non-production traffic and anonymous sentinel identities are excluded. Live exact counts. Defaults to 12 calendar months ending with the current month; explicit ranges may include at most 13 months. Requires a secret API key.

Parameters

  • from in query
  • to in query

Responses

  • 200 Monthly unique visitors
  • 400 Invalid month or range
  • 401
  • 403
  • 429
  • 503 Analytics or shop timezone temporarily unavailable; never reported as zero
cURL
curl \
  https://app.drip-apex.com/api/v1/usage/unique-visitors \
  -H "Authorization: Bearer <apex-secret-key>"

Brand

GET

Get brand context

/brand-context

Returns the compiled shop brand context for external agents and developer workflows. Only kept v2 about/write content is included, with categories and content added to the existing response. Proposals, brand-book fields and internal Apex notes are never included.

Responses

  • 200 Compiled brand context
  • 401 Unauthorized
  • 403 API key is missing experiment read permission
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/brand-context \
  -H "Authorization: Bearer <apex-secret-key>"

Commerce

GET

Look up Shopify products for commerce drafts

/commerce/catalog

Parameters

  • q in query
  • handle in query
  • product_id in query
  • cursor in query
  • limit in query

Responses

  • 200 Catalog lookup
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422
  • 429
  • 502 Shopify catalog read failed or returned an invalid price, currency or relationship (codes catalog_upstream_error, catalog_price_invalid, catalog_currency_unavailable, catalog_relationships_unavailable)
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/commerce/catalog \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read consent gating settings

/consent

Responses

  • 200 Consent settings
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/consent \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update consent gating settings

/consent

Request body

{
  applyCanonicalSplit?: true
  consentMode?: object
}

Responses

  • 200 Updated consent settings
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/consent \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "applyCanonicalSplit": true,
  "consentMode": {}
}'

Environments

GET

List runtime environments and domain mappings

/environments

Responses

  • 200 Environment settings
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/environments \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update runtime environments and domain mappings

/environments

Request body

{
  environments?: Environments
  expected_version?: string (date-time) | null
}

Responses

  • 200 Updated environment settings and edge precompile outcome
  • 400 The environment patch is invalid, including when the active key is absent from the effective environment items.
  • 401
  • 403
  • 409 Runtime settings changed since the supplied version was read; re-read and retry.
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/environments \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expected_version": "example_expected_version"
}'

Tracking

GET

Export raw events

/events/export

Exports a shop's raw events in an absolute window using stable keyset pagination.

Parameters

  • from in query required
  • to in query required
  • experiment in query
  • event_type in query - Comma-separated event types.
  • qa in query
  • limit in query
  • cursor in query - Opaque cursor returned by the previous page.

Responses

  • 200 Event export page
  • 400 Invalid window, filter, limit, or cursor. invalid_window reasons: invalid_timestamp, to_not_after_from, window_too_long, or to_in_future.
  • 401 Unauthorized
  • 403 Missing experiment read permission
  • 429
  • 503
  • 504 Tinybird query timed out
cURL
curl \
  https://app.drip-apex.com/api/v1/events/export \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Discover the observed tracking schema

/tracking/discovery

Aggregates consented-session QA diagnostic events (qa_mode=1) and normal-traffic pageview denominators (qa_mode=0) for the authenticated API key's shop.

Parameters

  • minutes in query
  • limit in query

Responses

  • 200 Observed tracking schema report with URL push/pageview counts, timing percentiles, type-collision flags, and ranked storage keys
  • 401 Unauthorized
  • 403 API key is missing experiment read permission
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/tracking/discovery \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Read shop tracking health

/tracking/health

Returns independent installation, event, commerce, experiment-delivery and settings checks for the selected shop. Unavailable sources are explicit unknown checks and never healthy defaults.

Responses

  • 200 Per-shop tracking health
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/tracking/health \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Measure forced-preview versus natural decision latency before launch

/tracking/latency

Shop-scoped sdk_perf diagnostic buckets for page loads (perf_event_kind = 'init') only; route_change events are excluded. Forced contains the requested experiment in forced_experiment_ids. Natural includes shop loads with qa_mode=0 and no forced IDs. Optional QA session includes all loads for that session. Percentiles are null below five measured samples. Analytics failures return 503, never empty success.

Parameters

  • experiment_id in query required - Shop-owned experiment UUID, public ID, or test key.
  • days in query
  • qa_session_id in query

Responses

  • 200 Decision latency summary, milliseconds
  • 400 Missing experiment or invalid window
  • 401 Unauthorized
  • 403 Missing experiments:read scope
  • 404 Experiment not found in this shop
  • 429 Rate limit exceeded
  • 503 Decision latency analytics unavailable
cURL
curl \
  https://app.drip-apex.com/api/v1/tracking/latency \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Simulate data-layer triggers

/tracking/simulate-trigger

Pure dry run of dataLayerTriggers against payloads. Does not persist or emit events.

Request body

{
  payloads: any[]
  triggers: object[]
}

Responses

  • 200 Side-effect-free simulation verdict
  • 400
  • 401
  • 403
  • 413 Request body exceeds 64KB
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/tracking/simulate-trigger \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "payloads": [],
  "triggers": []
}'
GET

Verify live tracking events

/tracking/verify

Returns recent Tinybird events for the API key's shop so agents can verify revenue, pageview, and custom tracking setup from the CLI.

Parameters

  • identity in query - Exact visitor_id or user_id, trimmed. Use the device id before identify and the app user id after identify. With identity, minutes defaults to 60 and is capped at 1440 (24 hours).
  • minutes in query - Defaults to 120, or 60 with identity. Maximum 10080, or 1440 with identity.
  • limit in query
  • experiment_id in query
  • event_type in query - Comma-separated event type filter.

Responses

  • 200 Tracking verification summary
  • 400 Identity must contain 1 to 128 characters after trimming
  • 401 Unauthorized
  • 403 API key is missing experiment read permission
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/tracking/verify \
  -H "Authorization: Bearer <apex-secret-key>"

Forms

GET

List tracked forms

/forms

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • range in query
  • split in query
  • device in query
  • country in query
  • include_qa in query
  • include_removed in query

Responses

  • 200 List tracked forms
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/forms \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Detect forms on a shop page

/forms/detect

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Request body

{
  url: string (uri)
}

Responses

  • 200 Detected forms
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 502 The page could not be inspected
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/forms/detect \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com"
}'
GET

Get a tracked form

/forms/{key}

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required
  • range in query
  • split in query
  • device in query
  • country in query
  • include_qa in query
  • include_removed in query

Responses

  • 200 Get a tracked form
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/forms/apx_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Create or restore a tracked form

/forms/{key}

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required

Request body

{ $ref: "FormInput" }

Responses

  • 200 Create or restore a tracked form
  • 201 Create or restore a tracked form
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/forms/apx_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Remove a tracked form

/forms/{key}

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required

Responses

  • 200 Remove a tracked form
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/forms/apx_123 \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Form errors ranked by abandonment

/forms/{key}/errors

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required
  • range in query
  • split in query
  • device in query
  • country in query
  • include_qa in query
  • include_removed in query

Responses

  • 200 Form errors ranked by abandonment
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/forms/apx_123/errors \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Form fields

/forms/{key}/fields

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required
  • range in query
  • split in query
  • device in query
  • country in query
  • include_qa in query
  • include_removed in query

Responses

  • 200 Form fields
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/forms/apx_123/fields \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Form field detail

/forms/{key}/fields/{identity}

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required
  • identity in path required
  • range in query
  • split in query
  • device in query
  • country in query
  • include_qa in query
  • include_removed in query

Responses

  • 200 Form field detail
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/forms/apx_123/fields/example_identity \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Label or merge a field

/forms/{key}/fields/{identity}

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required
  • identity in path required

Request body

{
  label?: string | null
  merged_into?: string | null
}

Responses

  • 200 Label or merge a field
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/forms/apx_123/fields/example_identity \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "example_label",
  "merged_into": "example_merged_into"
}'
GET

Form session identity paths

/forms/{key}/sessions

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required
  • range in query
  • split in query
  • device in query
  • country in query
  • include_qa in query
  • include_removed in query
  • outcome in query
  • field in query - Only sessions whose field path includes this field identity.
  • with_replay in query - `1` lists only sessions that have a replay.
  • limit in query
  • cursor in query

Responses

  • 200 Form session identity paths
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/forms/apx_123/sessions \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Form summary and checkout steps

/forms/{key}/summary

Shop-scoped form analytics. Read failures return 503; rates with no denominator are null. Split results describe versions without deciding a winner.

Parameters

  • key in path required
  • range in query
  • split in query
  • device in query
  • country in query
  • include_qa in query
  • include_removed in query

Responses

  • 200 Form summary and checkout steps
  • 400
  • 401
  • 403
  • 404
  • 409
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/forms/apx_123/summary \
  -H "Authorization: Bearer <apex-secret-key>"

Funnels

GET

Run funnel analysis

/funnels/{id}/run

Parameters

  • id in path required
  • days in query

Responses

  • 200 Funnel run
  • 400
  • 401
  • 403
  • 404
  • 429
  • 502 Analysis unavailable
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/funnels/exp_123/run \
  -H "Authorization: Bearer <apex-secret-key>"

Goals

GET

List goals

/goals

Returns a paginated list of goals for the shop.

Parameters

  • cursor in query
  • include_archived in query - Include archived goals.
  • limit in query

Responses

  • 200 Paginated list of goals
  • 401 Unauthorized
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/goals \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Create goal

/goals

Creates a new goal. Click goals require page_id plus selector or text_match. Pageview goals require page_id or url_pattern. Custom goals require event_name. Line-item filters are accepted for revenue goals and custom checkout_started/add_to_cart funnel-event goals.

Request body

{
  capture?: GoalCapture[] | null
  count_once?: boolean = false
  event_name?: string | null
  filter?: GoalFilter | null
  measurement?: "conversion" | "value"
  name: string
  page_id?: string (uuid) | null
  selector?: string | null
  text_match?: string | null
  type: "click" | "pageview" | "custom" | "revenue"
  url_pattern?: string | null
  value_property?: string | null
  value_unit?: "currency" | "number" | "duration_seconds" | "percent" | null
}

Responses

  • 201 Goal created
  • 400 Validation error; a click goal without page_id returns code: page_required with a next hint explaining how to choose a page.
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/goals \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "example_name",
  "type": "click"
}'
GET

Get goal

/goals/{id}

Returns a single goal by ID.

Parameters

  • id in path required

Responses

  • 200 Goal details
  • 404 Goal not found
cURL
curl \
  https://app.drip-apex.com/api/v1/goals/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update goal

/goals/{id}

Partially updates a goal. Line-item filters are accepted for revenue goals and custom checkout_started/add_to_cart funnel-event goals. Custom goals may not listen on reserved wire event names (purchase, pageview, ...): the resulting type/event_name combination is validated against the stored row and rejected with 400 (funnel-event goal names are the sanctioned exception). A material goal definition change clears QA approval and launch readiness on every referencing test in the same transaction, moving ready-to-launch tests back to draft. Each affected test receives an activity entry naming the goal and changed fields. Name-only, description-only and unchanged saves preserve readiness. An activity write failure rolls back the goal edit.

Parameters

  • id in path required

Request body

{
  capture?: GoalCapture[] | null
  count_once?: boolean
  event_name?: string | null
  filter?: GoalFilter | null
  measurement?: "conversion" | "value"
  name?: string
  page_id?: string (uuid) | null
  selector?: string | null
  text_match?: string | null
  type?: "click" | "pageview" | "custom" | "revenue"
  url_pattern?: string | null
  value_property?: string | null
  value_unit?: "currency" | "number" | "duration_seconds" | "percent" | null
}

Responses

  • 200 Goal updated
  • 400 Validation error; a click goal without page_id returns code: page_required with a next hint explaining how to choose a page.
  • 404 Goal not found
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/goals/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'
DELETE

Archive goal

/goals/{id}

Archives an unused goal. Refuses goals referenced by experiment links, personalizations, persisted test plans or workspace metric configuration; returns same-shop test ids, names and statuses.

Parameters

  • id in path required

Responses

  • 200 Goal deleted
  • 404 Goal not found
  • 409 Goal is in use. Remove its references before archiving.
cURL
curl -X DELETE \
  https://app.drip-apex.com/api/v1/goals/exp_123 \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Restore goal

/goals/{id}/restore

Parameters

  • id in path required

Responses

  • 200 Goal restored
  • 404 Goal not found
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/goals/exp_123/restore \
  -H "Authorization: Bearer <apex-secret-key>"

Inbox

GET

List the chat messages of an idea or experiment

/inbox/messages

Pass experiment_id or backlog_item_id. An experiment linked to an idea shares the idea's thread. The shop-wide account thread is retired and answers 410.

Parameters

  • experiment_id in query
  • backlog_item_id in query

Responses

  • 200 Inbox messages
  • 400
  • 403
  • 404
  • 410 The account thread is retired
cURL
curl \
  https://app.drip-apex.com/api/v1/inbox/messages \
  -H "Authorization: Bearer <apex-secret-key>"
POST

Send a chat message to an idea or experiment thread

/inbox/messages

thread_type idea needs backlog_item_id; experiment needs experiment_id and lands in the idea's thread when the experiment is linked to one. account answers 410.

Request body

{
  backlog_item_id?: string (uuid) | null
  body: string
  experiment_id?: string (uuid) | null
  thread_type?: "account" | "experiment" | "idea" = "account"
}

Responses

  • 201 Message created
  • 400
  • 403
  • 404
  • 410 The account thread is retired
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/inbox/messages \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "body": "example_body",
  "thread_type": "account"
}'
POST

Mark inbox channels read

/inbox/read-state

Request body

any

Responses

  • 200 Read state updated
  • 400
  • 403
  • 404
  • 410 The account thread is retired
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/inbox/read-state \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{}'

Inspection

POST

Inspect a live shop page

/inspect

One-shot read-only Browserbase inspection without an experiment or variation. User keys require shops:read and shop experiment-read permission; shop keys require experiments:read. X-Apex-Shop selects the user key's authorized shop. Each route call consumes one UTC-day inspection (default allowance 10). Composite page and element commands run their paired inspections in one session. Remote browsers share a two-session shop limit and 10-minute TTL.

Request body

{
  args?: object = {}
  command: "page" | "element" | "page_map" | "sections" | "dom" | "styles" | "screenshot"
  timeoutMs?: integer = 8000
  url: string (uri)
  viewport?: "desktop" | "mobile" = "desktop"
}

Responses

  • 200 Bridge inspection result, with capped base64 PNG or JPEG data URLs for screenshots
  • 400 Invalid request or missing shop context
  • 401 Unauthorized
  • 403 Insufficient permission or URL outside the shop's hosts
  • 409 Remote page inspection is disabled
  • 429 Daily inspection allowance, remote session capacity or key rate limit reached; error.message contains the reason
  • 502 Browser returned no usable result or screenshot exceeded the size limit
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/inspect \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "command": "page",
  "url": "https://example.com"
}'

Install Domains

GET

Read install domains and storefront origins

/install-domains

Responses

  • 200 Install domains
  • 401
  • 403
  • 404 The API key's shop was not found.
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/install-domains \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Replace install domains

/install-domains

Request body

{
  domains: string[]
  ifRevision?: string
}

Responses

  • 200 Updated install domains and edge precompile outcome
  • 400
  • 401
  • 403
  • 404 The API key's shop was not found.
  • 409 Install domains changed since the supplied revision was read; re-read and retry.
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/install-domains \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "domains": [],
  "ifRevision": "example_ifRevision"
}'

Install

POST

Assess a public storefront for Apex installation

/install/assess

Loads one public storefront through the guarded outbound fetch path and drafts an Apex installation assessment and client guide.

Request body

{
  shop_id?: string (uuid)
  url: string (uri)
}

Responses

  • 200 Storefront installation assessment
  • 400
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/install/assess \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "shop_id": "uuid-here",
  "url": "https://example.com"
}'

User keys

GET

Inspect the current user API key identity

/me

Responses

  • 200 User key identity and live organization role
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/me \
  -H "Authorization: Bearer <apex-secret-key>"
GET

List every organization and shop available to a user API key

/me/context

Responses

  • 200 User key context with effective organization and shop roles
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/me/context \
  -H "Authorization: Bearer <apex-secret-key>"

Notifications

GET

Read ready email recipients and current shop members

/notifications/ready/email

User API key with team:read (GET) or team:write (PUT), plus members:manage on the pinned shop. No master notification setting exists. Email off clears recipients; Slack off removes only experiment.ready_to_launch. Slack must already be connected, and its pause state is preserved.

Parameters

  • shop_id in query required
  • x-apex-shop in header required - Must match shop_id

Responses

  • 200 Current settings
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422 Recipient is not a current shop member
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/notifications/ready/email \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Replace ready email recipients (empty means off)

/notifications/ready/email

User API key with team:read (GET) or team:write (PUT), plus members:manage on the pinned shop. No master notification setting exists. Email off clears recipients; Slack off removes only experiment.ready_to_launch. Slack must already be connected, and its pause state is preserved.

Parameters

  • shop_id in query required
  • x-apex-shop in header required - Must match shop_id

Request body

{
  recipient_user_ids: string (uuid)[]
  version: integer
}

Responses

  • 200 Current settings
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422 Recipient is not a current shop member
  • 429
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/notifications/ready/email \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "recipient_user_ids": [],
  "version": 1
}'
GET

Read the ready Slack event toggle

/notifications/ready/slack

User API key with team:read (GET) or team:write (PUT), plus members:manage on the pinned shop. No master notification setting exists. Email off clears recipients; Slack off removes only experiment.ready_to_launch. Slack must already be connected, and its pause state is preserved.

Parameters

  • shop_id in query required
  • x-apex-shop in header required - Must match shop_id

Responses

  • 200 Current settings
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422 Recipient is not a current shop member
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/notifications/ready/slack \
  -H "Authorization: Bearer <apex-secret-key>"
PUT

Toggle the ready Slack event on an existing destination

/notifications/ready/slack

User API key with team:read (GET) or team:write (PUT), plus members:manage on the pinned shop. No master notification setting exists. Email off clears recipients; Slack off removes only experiment.ready_to_launch. Slack must already be connected, and its pause state is preserved.

Parameters

  • shop_id in query required
  • x-apex-shop in header required - Must match shop_id

Request body

{
  ready_to_launch: boolean
  version: string (date-time)
}

Responses

  • 200 Current settings
  • 400
  • 401
  • 403
  • 404
  • 409
  • 422 Recipient is not a current shop member
  • 429
  • 503
cURL
curl -X PUT \
  https://app.drip-apex.com/api/v1/notifications/ready/slack \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "ready_to_launch": false,
  "version": "example_version"
}'

Roadmap

GET

List approved roadmap ideas

/roadmap/approved

Returns approved backlog/roadmap ideas ready to build in Apex. This is the API used by `apex roadmap approved`.

Parameters

  • limit in query
  • cursor in query
  • q in query - Search title, description, hypothesis, brief, and research notes.
  • sort in query - Order field. `apex_score` (Likely to win, the headline score) sorts unscored ideas last. `computed_score` is deprecated and kept for older callers.
  • direction in query
  • has_experiment in query - When false, only returns approved ideas without a linked experiment. When true, only returns ideas with a linked experiment.

Responses

  • 200 Approved roadmap ideas
  • 401 Unauthorized
  • 403 API key is missing backlog read permission
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/roadmap/approved \
  -H "Authorization: Bearer <apex-secret-key>"
GET

Get roadmap build handoff

/roadmap/items/{id}/handoff

Returns the full build brief for an approved roadmap item, including hypothesis, target page, visual references, scoring, and Apex CLI workspace commands.

Parameters

  • id in path required

Responses

  • 200 Versioned roadmap build handoff
  • 401 Unauthorized
  • 403 API key is missing backlog read permission
  • 404 Roadmap item not found
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/roadmap/items/exp_123/handoff \
  -H "Authorization: Bearer <apex-secret-key>"

Runtime settings

GET

Read public runtime settings

/runtime-settings

Responses

  • 200 Parsed runtime settings with shop identity and opaque version
  • 401
  • 403
  • 429
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/runtime-settings \
  -H "Authorization: Bearer <apex-secret-key>"
PATCH

Update data layer triggers, setup capture, or project code

/runtime-settings

Request body

{
  dataLayerTriggers?: DataLayerTrigger[]
  expectedVersion?: string | null
  projectCode?: object
  setupCapture?: object
}

Responses

  • 200 Updated runtime settings and precompile result
  • 400
  • 401
  • 403
  • 409
  • 429
  • 503
cURL
curl -X PATCH \
  https://app.drip-apex.com/api/v1/runtime-settings \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "dataLayerTriggers": [],
  "expectedVersion": "example_expectedVersion",
  "projectCode": {},
  "setupCapture": {}
}'
POST

Apply a server-owned runtime integration recipe

/runtime-settings/recipes

Request body

{
  expectedVersion?: string | null
  params?: object
  recipeId: "customDataLayerPurchase" | "ga4Ecommerce"
}

Responses

  • 200 Updated runtime settings and precompile result
  • 400
  • 401
  • 403
  • 409
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/runtime-settings/recipes \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "expectedVersion": "example_expectedVersion",
  "params": {},
  "recipeId": "customDataLayerPurchase"
}'

SOS

POST

Create SOS ticket

/sos

Creates a customer bug report or help request for shop API keys. User API keys belonging to a DRIP platform administrator may also create internal SOS types.

Request body

{
  affected_url?: string (uri) | null
  attachment_paths?: string[]
  backlog_item_id?: string (uuid) | null
  description: string
  expected_behavior?: string
  experiment_id?: string (uuid) | null
  idempotency_key?: string
  loom_url?: string (uri) | null
  reproduction_steps?: string | null
  sub_class?: string | null
  type: "full_handover" | "design" | "development" | "qa" | "config" | "bug" | "help"
}

Responses

  • 200 Reused internal SOS ticket or deduplicated customer ticket receipt. Replaying an unconfirmed customer report with the same key retries safely and may return its confirmed dedup receipt.
  • 201 Internal SOS ticket or customer ticket created
  • 401 Unauthorized
  • 403 API key is missing permission or requested an internal SOS type
  • 429 Rate limit exceeded
  • 502 Jira create failed or remains unconfirmed (submission_unconfirmed). A replay with the same key searches for the existing ticket before creating again and may return a 200 dedup receipt.
  • 503 SOS is disabled
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/sos \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "example_description",
  "type": "full_handover"
}'
POST

Get signed SOS media upload targets

/sos/attachment-uploads

Upload directly to private Storage with PUT to signed_url, then send the returned paths as attachment_paths to POST /sos. Images are limited to 10 MB, videos to 50 MB, and each report to five files. Grants use Storage's default expiry. Requires the same support capability as POST /sos. Publishable keys cannot upload.

Request body

{
  files: object[]
}

Responses

  • 200 Signed upload targets
  • 400 Invalid media declaration
  • 401 Unauthorized
  • 403 Publishable key, inaccessible shop, or missing support capability
  • 429 Rate limit exceeded
  • 502 Storage grant failed
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/sos/attachment-uploads \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "files": []
}'
GET

List my SOS tickets

/sos/mine

Lists SOS tickets created by the current API key.

Responses

  • 200 SOS tickets
  • 401 Unauthorized
  • 403 API key is missing experiment read permission
  • 429 Rate limit exceeded
  • 503
cURL
curl \
  https://app.drip-apex.com/api/v1/sos/mine \
  -H "Authorization: Bearer <apex-secret-key>"

Telemetry

POST

Record a CLI invocation

/telemetry/cli

Accepts user and secret shop keys without a write scope. Duplicate invocation IDs are a no-op.

Request body

{
  agent_host?: string
  api_call_timings?: object[]
  api_calls?: integer = 0
  attempted?: string
  client_version: string
  command: string
  credential_kind?: "user" | "shop" | "env"
  duration_ms: integer
  entity_id?: string (uuid)
  entity_type?: "experiment" | "landing_page"
  error_class?: "ok" | "validation" | "auth" | "not_found" | "rate_limited" | "conflict" | "server" | "network" | "timeout" | "confirmation_required" | "check_failed" | "unknown_command" | "internal"
  exit_code: integer
  first_error_code?: string
  first_error_request_id?: string | null
  first_error_route?: string
  first_error_status?: integer
  invocation_id: string (uuid)
  json_mode?: boolean = false
  node_version?: string
  option_names?: string[] = []
  org_id?: string (uuid)
  platform?: string
  session_id?: string
  shop_id?: string (uuid)
  tty?: boolean = false
}

Responses

  • 202 Response
  • 400
  • 401
  • 403
  • 429
  • 503
cURL
curl -X POST \
  https://app.drip-apex.com/api/v1/telemetry/cli \
  -H "Authorization: Bearer <apex-secret-key>" \
  -H "Content-Type: application/json" \
  -d '{
  "client_version": "example_client_version",
  "command": "example_command",
  "duration_ms": 1,
  "exit_code": 1,
  "invocation_id": "uuid-here"
}'