Skip to main content
Version: Beta

Introduction

The Extraction Rules API provides programmatic access to create, read, update, and archive extraction rules. Extraction rules define how Fullstory captures structured data from your website or app — pulling values from page elements, URLs, and other sources into named properties for analytics.

Authentication

The HTTP API requires an API key that you can generate from the Fullstory app. The API key must have Admin or Architect level permissions for mutation operations. The header value takes the form "Basic {YOUR_API_KEY}".

Security Scheme Type:

apiKey

Header parameter name:

Authorization

Extraction Rules API Overview

Extraction rules pull structured data from page elements, URLs, or static values into named properties for analytics and segmentation. Each rule has a source (where to look), a scope (where to write the result), and one or more mappings (what to extract).

The API is archive-only. ArchiveExtractionRule stops a rule from capturing data but preserves it for audit history; UnarchiveExtractionRule restores it. There is no public delete RPC — permanent deletion is intentionally UI-only.

Concepts

Source — where the data comes from

A rule has exactly one source:

  • element (ElementSource) — matches page elements by selector. Each selector targets one or more platform values (PLATFORM_WEB, PLATFORM_IOS, PLATFORM_ANDROID) and carries a platform-specific selector string: a CSS selector for web, the view class name for iOS, or the fragment class name for Android. Multiple selectors in one rule combine with OR semantics.
  • url (UrlSource) — matches the page URL. Set any: true to consider every URL; per-mapping URL component selection happens in UrlValue (see below).

Named elements. Some SCOPE_ELEMENT rules in your org may anchor on a named element (named_element_id or child_named_element_id). The public API surfaces those fields so existing rules round-trip through Get / List / PUT / PATCH. The rules:

  • Creates always use selectors. Do not set named_element_id or child_named_element_id when creating a new rule. Configure named elements in Fullstory first if you need them.
  • Updates can drop, but not add, named-element anchoring. PATCH or PUT may replace named-element anchoring with selectors. They cannot introduce named-element anchoring on a rule that was stored without it — make that change in Fullstory.
  • SCOPE_PAGE and SCOPE_USER rules cannot use named-element anchoring at all. Setting named_element_id or child_named_element_id on those scopes is rejected.

Scope — where the data goes

A rule has exactly one scope, applied to all of its mappings:

  • SCOPE_PAGE — writes to a page-scoped property (page name or a custom page property).
  • SCOPE_USER — writes to a user-scoped property (uid, email, display name, or a custom user property).
  • SCOPE_ELEMENT — writes to an element-scoped property. Element-scoped rules carry extra constraints; see below.

Constraints when scope is SCOPE_ELEMENT

SCOPE_ELEMENT rules have a few extra requirements that page- and user-scoped rules do not:

  • Exactly one mapping per rule. A SCOPE_ELEMENT rule must contain exactly one mappings entry.
  • Element source only — no URL source. A SCOPE_ELEMENT rule must read from a matched element. URL sources are not valid here.
  • Attribute-only mappings when the anchor is loose. When the element anchor is match_any_selector or carries a child_match, every mapping must read from an HTML attribute (element_value.attribute). inner_text and static_value are not supported in those configurations.
  • element_value.attribute.include_ancestors is honored only on SCOPE_ELEMENT rules. When set, attribute resolution walks ancestors from the extraction basis and the nearest match wins.

Mapping — what to extract and where to write it

Each mapping has three parts:

  • value source — exactly one of element_value, url_value, or static_value.
    • element_value reads from the matched element. Use attribute to read an HTML attribute by name, or set inner_text: true to read the element's text content.
    • url_value reads a piece of the URL. Set one of the boolean flags full, path, host, or fragment to true, or set the string field query to the parameter name (for example "utm_campaign").
    • static_value writes a fixed string regardless of page content.
  • target — exactly one of the built-in Fullstory properties (api_name, uid, email, display_name) or a custom Property identified by name and data_type.
  • operation (optional) — a ValueProducer transform applied to the raw value before it is written. Today the only supported transform is a regex (regex.expression, with an optional regex.format string referencing capture groups like $1).

Active rule limits

Each org has a per-scope active_limit. Archiving a rule frees a slot; unarchiving consumes one. ListExtractionRules.usage_by_scope reports active_limit and active_count for each scope so callers can detect when an org is at the limit before attempting to create or unarchive.

Optimistic concurrency (etag)

Every rule has an opaque etag set by the server on read. Pass it on UpdateExtractionRule (PATCH) or Replace Extraction Rule (PUT). If the rule has changed since you read it, the server returns HTTP 409 Conflict — re-fetch, reconcile, and retry.

Updating rules: PATCH vs PUT

Two update endpoints sit on the same resource path:

  • PATCH /v2beta/extraction/rules/{id} (UpdateExtractionRule) — partial update. Send any subset of name, description, source, and mappings; omitted fields stay as they are. source and mappings are whole-field replacements when set: sending one mapping does not append, it overwrites the list. scope is immutable on PATCH — to change it, archive the rule and create a new one.
  • PUT /v2beta/extraction/rules/{id} (Replace Extraction Rule) — whole-resource replace. Send the complete rule body; every editable field is overwritten and unset fields are cleared.

Both honour the same etag flow. Choose PUT when you already hold the full intended rule state (for example, after editing it in your own UI). Choose PATCH when you only know the deltas.

PUT is friendly to a Get → mutate → PUT round-trip: server-managed fields on the rule body (created_at, updated_at, etag, archived) are silently ignored on input, so you can echo a Get response straight back without stripping fields. The body's id, if set, must agree with the id in the path.

Archive vs delete

This API is archive-only. ArchiveExtractionRule stops a rule from capturing data but preserves it for historical context. UnarchiveExtractionRule restores it (subject to active_limit). Permanent deletion is UI-only.

Unrepresented rules

Some legacy rules in an org cannot be modeled by the public API surface today. They count toward active_count but do not appear in the rules array. The unrepresented_count field on each scope in usage_by_scope reports how many rules matching this list request fall into that category. Manage those rules from the Fullstory UI.

Worked Example: Capture product price from a cart element

This rule watches the cart summary on the web app, reads the data-price attribute off the matched element, strips the leading currency symbol with a regex, and writes the result into a custom page-scoped property product_price of type DATA_TYPE_FLOAT.

{
"rule": {
"name": "Capture Product Price",
"description": "Extracts the product price from the cart summary element",
"scope": "SCOPE_PAGE",
"source": {
"element": {
"selectors": [
{
"platform": ["PLATFORM_WEB"],
"selector": "#cart-summary .price"
}
]
}
},
"mappings": [
{
"element_value": {
"attribute": {
"attribute": "data-price"
}
},
"target": {
"custom": {
"name": "product_price",
"data_type": "DATA_TYPE_FLOAT"
}
},
"operation": {
"regex": {
"expression": "^(\\d+\\.\\d{2})"
}
}
}
]
}
}

Worked Example: Capture campaign from URL query parameter

This rule reads the utm_campaign query parameter from any page URL and writes it into a custom user-scoped property acquisition_campaign of type DATA_TYPE_STRING.

{
"rule": {
"name": "Capture utm_campaign",
"description": "Reads the utm_campaign query parameter into a user property",
"scope": "SCOPE_USER",
"source": {
"url": {
"any": true
}
},
"mappings": [
{
"url_value": {
"query": "utm_campaign"
},
"target": {
"custom": {
"name": "acquisition_campaign",
"data_type": "DATA_TYPE_STRING"
}
}
}
]
}
}