{"components":{"headers":{"IdempotentReplayed":{"description":"`true` when this is the stored answer to an earlier request with the same Idempotency-Key.","schema":{"enum":["true"],"type":"string"}}},"parameters":{"Cursor":{"description":"The next_cursor of the previous page.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}},"HeatmapBucketParam":{"in":"query","name":"bucket","required":true,"schema":{"$ref":"#/components/schemas/HeatmapBucket"}},"HeatmapFrom":{"description":"The period's start; with to, at most 93 days. Both left out is the seven days to now.","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":"string"}},"HeatmapPath":{"description":"The page's path, without a query string or fragment.","in":"query","name":"path","required":true,"schema":{"example":"/pricing","type":"string"}},"HeatmapTo":{"description":"The period's end, not included.","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":"string"}},"IdempotencyKey":{"description":"Makes the request safe to retry: the same key with the same request returns the first answer for 24 hours. Generate one per logical change. Required when the caller is a partner integration's managed key.","in":"header","name":"Idempotency-Key","required":false,"schema":{"pattern":"^[A-Za-z0-9._:-]{8,255}$","type":"string"}},"Limit":{"in":"query","name":"limit","required":false,"schema":{"default":50,"maximum":100,"minimum":1,"type":"integer"}},"OriginId":{"in":"path","name":"originId","required":true,"schema":{"format":"uuid","type":"string"}},"RecordingId":{"in":"path","name":"recordingId","required":true,"schema":{"format":"uuid","type":"string"}},"SessionId":{"in":"path","name":"sessionId","required":true,"schema":{"format":"uuid","type":"string"}},"SiteId":{"in":"path","name":"siteId","required":true,"schema":{"format":"uuid","type":"string"}}},"responses":{"Conflict":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The object already exists (`conflict`), or the site changed since it was read (`version_conflict`)."},"Forbidden":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The credential lacks the permission or scope, names another organisation, or belongs to a partner integration that is not active (`forbidden`, `insufficient_scope`, `integration_inactive`)."},"InvalidIdempotency":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The body is not valid JSON of the documented shape (`invalid_json`); the Idempotency-Key is malformed (`invalid_idempotency_key`); or a partner integration sent none (`idempotency_key_required`)."},"InvalidJSON":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The body is not one strict JSON value of the documented shape (`invalid_json`)."},"InvalidQuery":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"An unknown, repeated or malformed query parameter (`invalid_query`)."},"InvalidRequest":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A field is invalid, or a limit would be exceeded; the message names it (`invalid_request`, `limit_reached`)."},"NotFound":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No such object in this organisation (`not_found`)."},"RateLimited":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Too many requests; wait for Retry-After (`rate_limited`).","headers":{"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}}},"Unauthorized":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No credential, an invalid one, an expired partner integration key, or a delegated token where none is accepted (`unauthorized`, `invalid_api_key`, `api_key_expired`, `delegated_not_allowed`)."}},"schemas":{"Capabilities":{"properties":{"features":{"additionalProperties":{"type":"boolean"},"description":"Each feature and whether this deployment offers it now.","type":"object"},"limits":{"additionalProperties":{"type":"integer"},"description":"Each limit by name. sites_per_organisation and stored_bytes_per_organisation are this organisation's own; the second is what its unexpired recordings may hold before new recordings are refused, 10 GiB unless a managed partner's agreement sets another figure.","type":"object"},"schema_versions":{"additionalProperties":{"type":"integer"},"type":"object"}},"required":["features","limits","schema_versions"],"type":"object"},"ConfigVersion":{"properties":{"config":{"$ref":"#/components/schemas/SiteConfig"},"created_at":{"format":"date-time","type":"string"},"created_by":{"description":"The user id, or key:\u003cid\u003e for a platform key.","type":"string"},"id":{"format":"uuid","type":"string"},"sha256":{"description":"SHA-256 of the stored configuration document.","type":"string"},"site_id":{"format":"uuid","type":"string"},"version":{"type":"integer"}},"required":["id","site_id","version","config","sha256","created_by","created_at"],"type":"object"},"Epoch":{"properties":{"bytes":{"type":"integer"},"contiguous_through":{"description":"The highest sequence before the first gap; -1 when none.","type":"integer"},"created_at":{"format":"date-time","type":"string"},"has_checkpoint":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"index":{"description":"A page load or a restart from a fresh checkpoint.","type":"integer"},"max_sequence":{"type":"integer"}},"required":["id","index","contiguous_through","max_sequence","has_checkpoint","bytes","created_at"],"type":"object"},"Error":{"properties":{"error":{"properties":{"code":{"description":"Stable machine-readable code.","type":"string"},"docs":{"format":"uri","type":"string"},"message":{"description":"Human-readable explanation; do not parse.","type":"string"}},"required":["code","message","docs"],"type":"object"},"request_id":{"type":"string"}},"required":["error"],"type":"object"},"Heatmap":{"properties":{"background":{"anyOf":[{"$ref":"#/components/schemas/HeatmapBackground"},{"type":"null"}],"description":"The visit whose page at load the heatmap is drawn over; play it through the playback routes."},"backgrounds":{"description":"The visits the heatmap can be drawn over, background first: of the most common layout, at a width near the period's median, and of those the ones that scrolled furthest. At most 5.","items":{"$ref":"#/components/schemas/HeatmapBackground"},"type":"array"},"bands":{"properties":{"attention_share":{"description":"Per band from the top, the share of a visit's time it was on screen.","items":{"type":"number"},"type":"array"},"ms_per_visit":{"items":{"type":"number"},"type":"array"},"px":{"description":"The height of a band.","type":"integer"},"reach":{"description":"Per band, the share of visits that scrolled to its bottom.","items":{"type":"number"},"type":"array"}},"required":["px","attention_share","ms_per_visit","reach"],"type":"object"},"bucket":{"$ref":"#/components/schemas/HeatmapBucket"},"clicks":{"properties":{"cell_px":{"type":"integer"},"cells":{"items":{"$ref":"#/components/schemas/HeatmapCell"},"type":"array"},"elements":{"description":"The targets most clicked, named without text.","items":{"properties":{"cells":{"description":"The located clicks in the page grid (the fixed grid when fixed), per visit. Drawing them inside the target's box instead, take these out of cells or fixed_cells so they are not counted twice.","items":{"$ref":"#/components/schemas/HeatmapCell"},"type":"array"},"clicks":{"type":"integer"},"fixed":{"description":"Whether most of its clicks landed on it while fixed or sticky. located, offsets and cells cover those clicks only.","type":"boolean"},"located":{"description":"How many of its clicks carry where within the target they landed (SDK 0.5.0).","type":"integer"},"offsets":{"description":"A sample of the located clicks, where within the target each landed, 0 to 10000 across and down.","items":{"items":{"type":"integer"},"maxItems":2,"minItems":2,"type":"array"},"type":"array"},"per_visit":{"type":"number"},"selector":{"type":"string"}},"required":["selector","clicks","per_visit","fixed","located","offsets","cells"],"type":"object"},"type":"array"},"fixed_cells":{"items":{"$ref":"#/components/schemas/HeatmapCell"},"type":"array"},"fixed_per_visit":{"description":"Clicks on fixed or sticky elements, which belong to the screen rather than a place on the page.","type":"number"},"per_visit":{"type":"number"}},"required":["per_visit","fixed_per_visit","cell_px","cells","fixed_cells","elements"],"type":"object"},"from":{"format":"date-time","type":"string"},"layouts":{"description":"The layouts seen, most common first.","items":{"properties":{"hash":{"type":["string","null"]},"visits":{"type":"integer"}},"required":["hash","visits"],"type":"object"},"type":"array"},"moves":{"properties":{"cell_px":{"type":"integer"},"cells":{"description":"Milliseconds per visit the pointer rested in each cell.","items":{"$ref":"#/components/schemas/HeatmapCell"},"type":"array"}},"required":["cell_px","cells"],"type":"object"},"path":{"type":"string"},"sampled":{"description":"The visits the totals come from, at most 20000.","type":"integer"},"seconds_on_screen":{"properties":{"mean":{"type":"number"},"median":{"type":"number"}},"required":["mean","median"],"type":"object"},"small_sample":{"description":"Fewer than 30 visits.","type":"boolean"},"to":{"format":"date-time","type":"string"},"visibility_known":{"description":"The share of visits whose time excludes a hidden tab (SDK 0.5.0).","type":"number"},"visits":{"description":"Page visits in the period.","type":"integer"}},"required":["path","bucket","from","to","visits","sampled","small_sample","visibility_known","seconds_on_screen","layouts","background","backgrounds","bands","clicks","moves"],"type":"object"},"HeatmapBackground":{"properties":{"duration_ms":{"description":"How long the visit lasted.","type":"integer"},"epoch_index":{"type":"integer"},"layout_hash":{"type":["string","null"]},"offset_ms":{"description":"Where the visit starts in its recording, in milliseconds from the recording's first event, as the replay player counts them.","type":"integer"},"recording_id":{"format":"uuid","type":"string"},"started_at":{"format":"date-time","type":"string"},"viewport_height":{"type":"integer"},"viewport_width":{"type":"integer"}},"required":["recording_id","epoch_index","started_at","offset_ms","duration_ms","layout_hash","viewport_width","viewport_height"],"type":"object"},"HeatmapBucket":{"description":"By viewport width when the visit began, under 768, under 1280, and wider.","enum":["mobile","tablet","desktop"],"type":"string"},"HeatmapCell":{"description":"[x, y, value]: x cells from the centre of the viewport (negative to the left), y cells from the top of the page (of the viewport for fixed clicks), and the value per visit.","items":{"type":"number"},"maxItems":3,"minItems":3,"type":"array"},"HeatmapPage":{"properties":{"desktop":{"type":"integer"},"mobile":{"type":"integer"},"path":{"type":"string"},"tablet":{"type":"integer"},"visits":{"type":"integer"}},"required":["path","visits","mobile","tablet","desktop"],"type":"object"},"Installation":{"properties":{"public_key":{"type":"string"},"ready":{"description":"True when every step is done.","type":"boolean"},"site_id":{"format":"uuid","type":"string"},"snippet":{"description":"The script tag to install, once: it loads /sdk/loader.js, which is always the newest SDK release, so it never needs changing. Null when no SDK release or active key is available.","type":["string","null"]},"steps":{"items":{"properties":{"detail":{"type":"string"},"done":{"type":"boolean"},"id":{"enum":["site_active","collection_active","origin_verified","recording_enabled","recorder_released"],"type":"string"}},"required":["id","done","detail"],"type":"object"},"type":"array"}},"required":["site_id","public_key","ready","steps","snippet"],"type":"object"},"JourneyPage":{"allOf":[{"$ref":"#/components/schemas/ReplayPoint"},{"properties":{"at":{"format":"date-time","type":"string"},"dwell":{"description":"measured, the page reported its end; estimated, from the next page in the same tab; open, the visitor is still on it; unknown, neither.","enum":["measured","estimated","open","unknown"],"type":"string"},"dwell_ms":{"type":["integer","null"]},"ended_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"kind":{"description":"How the page came to be: a document load, a reload, back or forward, an in-page navigation, a page reported again when the next session started, or a page known only from a recording.","enum":["load","reload","back_forward","route","resume","recorded"],"type":"string"},"path":{"type":"string"},"query":{"description":"The query parameters the site keeps.","type":["string","null"]},"tab":{"description":"The tab, numbered from 1 in the order tabs first reported a page; 0 when unknown.","type":"integer"},"visible_ms":{"description":"How long the tab was visible on the page, when known.","type":["integer","null"]}},"required":["id","tab","kind","path","query","at","ended_at","visible_ms","dwell_ms","dwell"],"type":"object"}]},"LivePlayback":{"description":"What a live viewer needs beside the batches to say how far behind the visitor it is (plans/analytics-live-delay.md). clock and progress come from SDK 0.8.0 and later, and are null before the recorder has sent them.","properties":{"clock":{"description":"The collector's clock minus the visitor's, within error_ms either way, from the round trip that bounded it most tightly.","properties":{"error_ms":{"maximum":60000,"minimum":0,"type":"integer"},"offset_ms":{"format":"int64","type":"integer"}},"required":["offset_ms","error_ms"],"type":["object","null"]},"ended":{"description":"The recording has ended: nothing more will arrive.","type":"boolean"},"last_seen_at":{"description":"When the visitor's page was last heard from, on the collector's clock.","format":"date-time","type":"string"},"progress":{"description":"The recorder's newest progress report: every change up to through (the visitor's clock) is in a batch up to (epoch_index, sequence). watched says it was uploading at live pace. A quiet page sends one every two seconds while watched.","properties":{"epoch_index":{"minimum":0,"type":"integer"},"received_at":{"description":"The collector's clock.","format":"date-time","type":"string"},"sequence":{"minimum":0,"type":"integer"},"through":{"format":"date-time","type":"string"},"watched":{"type":"boolean"}},"required":["epoch_index","sequence","through","watched","received_at"],"type":["object","null"]},"sdk_version":{"description":"The SDK that recorded it; null before its first batch. A recorder before 0.8.0 reports neither clock nor progress.","type":["string","null"]}},"required":["ended","last_seen_at","sdk_version","clock","progress"],"type":"object"},"Meter":{"properties":{"billable":{"description":"How many are charged.","type":"integer"},"budget":{"description":"The monthly limit set for Ironfang Analytics before tax; null when it shares the account's.","type":["string","null"]},"charge":{"description":"The exact charge for used, before tax, in major units (for example \"1.25\").","type":"string"},"currency":{"description":"ISO 4217 currency code: GBP.","type":"string"},"exempt":{"description":"True when the account's Ironfang Analytics usage is counted and not charged.","type":"boolean"},"free_allowance":{"description":"Recordings the month includes free.","type":"integer"},"free_used":{"description":"How many of them the free allowance covers.","type":"integer"},"meter":{"enum":["analytics.recording"],"type":"string"},"remaining_free":{"description":"Free recordings neither used nor held.","type":"integer"},"reserved":{"description":"Recordings admitted and not yet ended; each holds its unit until then.","type":"integer"},"used":{"description":"Kept recordings counted in the month.","type":"integer"}},"required":["meter","free_allowance","used","free_used","billable","reserved","remaining_free","charge","currency","exempt","budget"],"type":"object"},"Origin":{"properties":{"checked_at":{"format":"date-time","type":"string"},"created_at":{"format":"date-time","type":"string"},"dns_record":{"description":"The TXT record to publish; absent for loopback origins.","properties":{"name":{"example":"_ironfang-analytics.shop.example.com","type":"string"},"type":{"enum":["TXT"],"type":"string"},"value":{"type":"string"}},"required":["type","name","value"],"type":"object"},"id":{"format":"uuid","type":"string"},"origin":{"example":"https://shop.example.com","type":"string"},"site_id":{"format":"uuid","type":"string"},"verification":{"enum":["pending","verified","not_required"],"type":"string"},"verification_token":{"type":"string"},"verified_at":{"format":"date-time","type":"string"}},"required":["id","site_id","origin","verification","created_at"],"type":"object"},"PlaybackChunk":{"properties":{"client_wall_time":{"description":"The browser's clock. From SDK 0.8.0 every change the page recorded up to it is in this batch or an earlier one; before, it was when the batch was compressed, a moment after it was cut.","format":"date-time","type":["string","null"]},"compressed_bytes":{"type":"integer"},"contains_full_snapshot":{"type":"boolean"},"encoding":{"enum":["gzip","identity"],"type":"string"},"epoch_index":{"type":"integer"},"event_count":{"type":"integer"},"expanded_bytes":{"type":"integer"},"monotonic_end_ms":{"type":"number"},"monotonic_start_ms":{"type":"number"},"received_at":{"description":"When the collector received the batch, on its clock.","format":"date-time","type":"string"},"sequence":{"type":"integer"}},"required":["epoch_index","sequence","encoding","compressed_bytes","expanded_bytes","event_count","contains_full_snapshot","monotonic_start_ms","monotonic_end_ms","client_wall_time","received_at"],"type":"object"},"RecordingDetail":{"allOf":[{"$ref":"#/components/schemas/RecordingRow"},{"properties":{"config_version":{"description":"The site configuration the recording was made under.","type":"integer"},"epochs":{"items":{"$ref":"#/components/schemas/Epoch"},"type":"array"},"ip_source":{"description":"cloudflare is the visitor's address as Cloudflare reported it; edge and peer are whoever connected, which is the visitor only when nothing sat in between.","enum":["cloudflare","edge","peer",null],"type":["string","null"]},"language":{"type":["string","null"]},"pixel_ratio":{"type":["number","null"]},"referrer":{"description":"Origin and path only; the query and fragment are never kept.","type":["string","null"]},"rrweb_version":{"type":["string","null"]},"screen":{"oneOf":[{"$ref":"#/components/schemas/Size"},{"type":"null"}]},"sdk_version":{"type":["string","null"]},"timezone":{"description":"IANA time zone the browser reported.","type":["string","null"]},"user_agent":{"description":"The full User-Agent header, at most 512 characters.","type":["string","null"]},"viewport":{"oneOf":[{"$ref":"#/components/schemas/Size"},{"type":"null"}]}},"required":["config_version","sdk_version","rrweb_version","ip_source","user_agent","viewport","screen","pixel_ratio","language","timezone","referrer","epochs"],"type":"object"}]},"RecordingRow":{"properties":{"browser":{"description":"Coarse class from the user agent.","type":["string","null"]},"bytes":{"description":"Compressed bytes stored.","type":"integer"},"chunks":{"type":"integer"},"country":{"description":"From Cloudflare, when the request came through it.","type":["string","null"]},"created_at":{"format":"date-time","type":"string"},"device":{"enum":["desktop","mobile","tablet",null],"type":["string","null"]},"duration_ms":{"description":"The recorded activity, in milliseconds: in each page load, from its first batch's start to its last batch's end by the browser's monotonic clock, summed.","type":"integer"},"end_reason":{"enum":["finished","idle_timeout","duration_cap","byte_cap","site_disabled",null],"type":["string","null"]},"ended_at":{"format":"date-time","type":["string","null"]},"entry_path":{"type":["string","null"]},"events":{"type":"integer"},"expires_at":{"format":"date-time","type":"string"},"first_received_at":{"format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"ip":{"description":"The visitor's IP address.","type":["string","null"]},"last_received_at":{"format":"date-time","type":["string","null"]},"last_seen_at":{"description":"The last batch or heartbeat. An open page sends a heartbeat every 30 seconds.","format":"date-time","type":"string"},"live":{"description":"Still open and heard from in the last 90 seconds: someone is on the page. Open but quiet means the visitor has most likely gone; the recording closes after 30 minutes without a word, because leaving a page cannot be told apart from moving to the next one.","type":"boolean"},"origin":{"type":"string"},"os":{"type":["string","null"]},"playable":{"description":"True once some epoch holds a checkpoint at its start.","type":"boolean"},"session_id":{"description":"The session the recording belongs to. Null only for a recording made before sessions that has not been placed in one yet.","format":"uuid","type":["string","null"]},"site_id":{"format":"uuid","type":"string"},"site_name":{"type":"string"},"state":{"enum":["pending","recording","ready","partial","unplayable"],"type":"string"},"visitor_id":{"description":"The browser that made the recording: a random id the recorder (SDK 0.3.0 and later) keeps in the site's localStorage. The same id on two recordings is the same browser, unless its storage was cleared in between. Null for recordings from older recorders.","format":"uuid","type":["string","null"]}},"required":["id","site_id","site_name","state","end_reason","playable","origin","entry_path","browser","os","device","country","ip","bytes","chunks","events","duration_ms","created_at","first_received_at","last_received_at","ended_at","expires_at","last_seen_at","live","visitor_id","session_id"],"type":"object"},"ReplayPoint":{"properties":{"offset_ms":{"description":"Where in that recording's replay it happened, when it can be played.","type":["integer","null"]},"recording_id":{"description":"The recording that saw this moment.","format":"uuid","type":["string","null"]},"replay":{"description":"Whether this moment can be watched: available; recording, in a recording still open; expired, deleted, discarded (too short or without interaction) or unplayable, a recording saw it and cannot be played; not_recorded, no recording covers it.","enum":["available","recording","expired","deleted","discarded","unplayable","not_recorded"],"type":"string"}},"required":["recording_id","offset_ms","replay"],"type":"object"},"SessionDetail":{"allOf":[{"$ref":"#/components/schemas/SessionRow"},{"properties":{"clock_offset_ms":{"description":"The server's clock minus the browser's, measured when the session started; journey and timeline times include it.","type":"integer"},"ip_source":{"enum":["cloudflare","edge","peer",null],"type":["string","null"]},"language":{"type":["string","null"]},"pages":{"items":{"$ref":"#/components/schemas/JourneyPage"},"type":"array"},"pixel_ratio":{"type":["number","null"]},"recordings":{"items":{"$ref":"#/components/schemas/SessionRecording"},"type":"array"},"screen":{"oneOf":[{"$ref":"#/components/schemas/Size"},{"type":"null"}]},"sdk_version":{"type":["string","null"]},"timezone":{"type":["string","null"]},"user_agent":{"type":["string","null"]},"viewport":{"oneOf":[{"$ref":"#/components/schemas/Size"},{"type":"null"}]}},"required":["sdk_version","ip_source","user_agent","viewport","screen","pixel_ratio","language","timezone","clock_offset_ms","pages","recordings"],"type":"object"}]},"SessionHeatmap":{"properties":{"heatmap":{"description":"The page and device asked for, or the defaults; null when the session has no heatmap for it.","oneOf":[{"$ref":"#/components/schemas/Heatmap"},{"type":"null"}]},"pages":{"description":"The pages with a heatmap, in the order the session first came to them, by device.","items":{"$ref":"#/components/schemas/HeatmapPage"},"type":"array"}},"required":["pages","heatmap"],"type":"object"},"SessionRecording":{"properties":{"created_at":{"format":"date-time","type":"string"},"discarded":{"description":"Why it was not kept.","enum":["empty","no_interaction","too_short",null],"type":["string","null"]},"duration_ms":{"type":"integer"},"end_reason":{"enum":["finished","idle_timeout","duration_cap","byte_cap","site_disabled",null],"type":["string","null"]},"ended_at":{"format":"date-time","type":["string","null"]},"entry_path":{"type":["string","null"]},"expires_at":{"format":"date-time","type":"string"},"from":{"description":"When its first event happened.","format":"date-time","type":["string","null"]},"id":{"format":"uuid","type":"string"},"live":{"type":"boolean"},"pages":{"description":"Pages of the journey it saw.","type":"integer"},"playable":{"type":"boolean"},"state":{"enum":["pending","recording","ready","partial","unplayable"],"type":"string"},"status":{"enum":["available","recording","expired","deleted","discarded","unplayable"],"type":"string"},"tab":{"description":"0 when the recorder did not name its tab (before SDK 0.6.0).","type":"integer"},"until":{"description":"When its latest event happened.","format":"date-time","type":["string","null"]}},"required":["id","tab","state","end_reason","playable","live","status","discarded","created_at","ended_at","from","until","duration_ms","expires_at","entry_path","pages"],"type":"object"},"SessionRow":{"properties":{"anonymised":{"description":"Its IP address, user agent, visitor id, error messages and event properties were cleared at the recording retention.","type":"boolean"},"browser":{"type":["string","null"]},"country":{"type":["string","null"]},"counts":{"properties":{"console_errors":{"description":"console.error calls (SDK 0.7.0).","type":"integer"},"custom":{"description":"Events the page tracked.","type":"integer"},"dropped":{"description":"Pages and other events beyond the session's limits, counted and not kept.","type":"integer"},"errors":{"description":"Uncaught errors and unhandled rejections.","type":"integer"},"errors_not_kept":{"description":"Errors and console errors past the per-page or per-session limits, counted and not kept.","type":"integer"},"events":{"description":"Every event, of every type.","type":"integer"},"failed_requests":{"description":"Requests that failed: an error answer, no answer, a timeout or a resource that did not load.","type":"integer"},"pages":{"type":"integer"},"replayable_pages":{"description":"Pages a recording that can still be played saw.","type":"integer"},"request_sample_percent":{"description":"The smallest share of successful requests kept while the session was reported; null when none were sampled.","type":["integer","null"]},"requests":{"description":"Requests kept: every outcome before SDK 0.7.0 only failed or slow ones.","type":"integer"},"requests_not_kept":{"description":"Requests past the per-page or per-session limits, counted and not kept.","type":"integer"},"requests_sampled_out":{"description":"Successful requests sampling left out.","type":"integer"},"tabs":{"type":"integer"}},"required":["pages","events","errors","console_errors","requests","failed_requests","custom","tabs","dropped","errors_not_kept","requests_not_kept","requests_sampled_out","request_sample_percent","replayable_pages"],"type":"object"},"device":{"enum":["desktop","mobile","tablet",null],"type":["string","null"]},"duration_ms":{"description":"From the start to the last activity.","type":"integer"},"end_reason":{"description":"idle_timeout, 30 minutes without the visitor doing anything; duration_cap, 12 hours from its start.","enum":["idle_timeout","duration_cap",null],"type":["string","null"]},"ended_at":{"format":"date-time","type":["string","null"]},"entry_path":{"type":["string","null"]},"exit_path":{"type":["string","null"]},"expires_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"ip":{"description":"Null once the session is anonymised.","type":["string","null"]},"last_activity_at":{"description":"The visitor's last activity in any tab.","format":"date-time","type":"string"},"live":{"description":"Active and heard from in the last 90 seconds.","type":"boolean"},"origin":{"type":"string"},"os":{"type":["string","null"]},"recording":{"properties":{"available":{"description":"Recordings that can be played.","type":"integer"},"live":{"description":"The open recording was heard from in the last 90 seconds.","type":"boolean"},"open_recording_id":{"description":"The recording still open, to watch live.","format":"uuid","type":["string","null"]},"status":{"description":"none, no recording; recording, one is still open; available, every page can be replayed; partial, some can; expired, deleted or unavailable, recordings exist and none can be played.","enum":["none","recording","available","partial","expired","deleted","unavailable"],"type":"string"},"total":{"type":"integer"}},"required":["status","total","available","open_recording_id","live"],"type":"object"},"referrer":{"description":"Origin and path only.","type":["string","null"]},"site_id":{"format":"uuid","type":"string"},"site_name":{"type":"string"},"source":{"description":"sdk, reported page by page by the SDK (0.6.0 and later); recording, known only from its recordings (older SDKs, and visits recorded before sessions), so only recorded pages are in it.","enum":["sdk","recording"],"type":"string"},"started_at":{"format":"date-time","type":"string"},"state":{"enum":["active","ended"],"type":"string"},"visitor_id":{"description":"Null once the session is anonymised.","format":"uuid","type":["string","null"]}},"required":["id","site_id","site_name","state","live","end_reason","source","started_at","last_activity_at","ended_at","expires_at","duration_ms","entry_path","exit_path","origin","referrer","browser","os","device","country","ip","visitor_id","anonymised","counts","recording"],"type":"object"},"Site":{"properties":{"config_version":{"description":"The configuration version in force.","type":"integer"},"created_at":{"format":"date-time","type":"string"},"disabled_at":{"format":"date-time","type":"string"},"external_id":{"description":"Your own identifier for the site, when one was given at creation.","type":["string","null"]},"id":{"format":"uuid","type":"string"},"name":{"type":"string"},"organisation_id":{"format":"uuid","type":"string"},"retention_days":{"description":"Days a recording is kept.","type":"integer"},"session_retention_days":{"description":"Days session history is kept, never fewer than retention_days. A session loses its IP address, user agent and visitor id after retention_days.","type":"integer"},"status":{"enum":["active","disabled"],"type":"string"},"timezone":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"version":{"description":"Name this in a PATCH.","type":"integer"}},"required":["id","organisation_id","name","external_id","status","timezone","retention_days","session_retention_days","version","config_version","created_at","updated_at"],"type":"object"},"SiteConfig":{"additionalProperties":false,"description":"What the site's recorder may capture. Whatever this says, anything that looks like a payment card number (13 to 19 digits passing the Luhn check) is masked wherever it appears, and card, CVV, password and one-time-code fields are blocked.","properties":{"analytics":{"additionalProperties":false,"description":"Session analytics, from SDK 0.6.0. Sessions are the root: a recording belongs to its session, so a site records only while it follows sessions, and may follow sessions without recording. The site's consent mode and excluded addresses apply as they do to recording.","properties":{"console_errors":{"default":true,"description":"Add console.error calls to sessions (SDK 0.7.0), their arguments serialised safely and scrubbed as error messages are. A version stored before this switch reads as false.","type":"boolean"},"enabled":{"default":true,"description":"Report every eligible visitor's pages from page load, recorded or not. Must be true while recording.enabled is (422 otherwise). A version stored before this rule that records reads as true.","type":"boolean"},"errors":{"default":true,"description":"Add uncaught errors and unhandled rejections to sessions.","type":"boolean"},"request_sample_percent":{"default":100,"description":"The share of successful requests kept, decided per request in the browser. Failures are never sampled. A session says the share it was kept at, and how many were left out.","maximum":100,"minimum":1,"type":"integer"},"requests":{"default":true,"description":"Add requests that fail (an answer of 400 or above, no answer, a timeout, a resource that did not load), that the page aborted, or that take 5 seconds or longer, to sessions.","type":"boolean"},"successful_requests":{"default":true,"description":"Add the rest, quick successes, to sessions too (SDK 0.7.0). A version stored before this switch reads as false.","type":"boolean"}},"type":"object"},"recording":{"additionalProperties":false,"properties":{"allowed_query_parameters":{"description":"Query parameters whose values are kept in recorded URLs while record_all_query_parameters is off; all others are removed.","items":{"pattern":"^[A-Za-z0-9_.\\-\\[\\]]{1,64}$","type":"string"},"maxItems":50,"type":"array"},"block_selectors":{"items":{"maxLength":256,"type":"string"},"maxItems":100,"type":"array"},"checkpoint_interval_seconds":{"default":60,"maximum":300,"minimum":15,"type":"integer"},"consent":{"default":"opt_out","description":"opt_out records from page load unless the page opts the visitor out with setConsent({replay: false}), which the browser remembers. required records nothing until the page grants replay consent; the site's snippet then carries data-consent=\"required\" so nothing is requested before it.","enum":["opt_out","required"],"type":"string"},"enabled":{"default":false,"type":"boolean"},"excluded_ip_ranges":{"description":"Visitor addresses that are never recorded, such as the site owner's own networks: public IPv4 or IPv6 addresses, or CIDR ranges such as 203.0.113.0/24 or 2001:db8:1:2::/64. A visitor inside one is refused at admission, and a recording already open from there does not continue. The list is not sent to pages; the collector's GET /v1/address tells a browser its own address and the range to add for it.","items":{"maxLength":64,"type":"string"},"maxItems":100,"type":"array"},"mask_inputs":{"default":false,"description":"Mask every form field's value and text typed into an editable region.","type":"boolean"},"mask_selectors":{"items":{"maxLength":256,"type":"string"},"maxItems":100,"type":"array"},"mask_text":{"default":false,"description":"Mask every text node; unmask_selectors then expose reviewed static text.","type":"boolean"},"max_compressed_mib":{"default":100,"maximum":100,"minimum":1,"type":"integer"},"max_duration_minutes":{"default":60,"maximum":60,"minimum":1,"type":"integer"},"record_all_query_parameters":{"default":false,"description":"Keep every query value in recorded URLs except those in removed_query_parameters. Off, only allowed_query_parameters keep theirs. Fragments are removed either way. Needs SDK 0.2.1 or later; older recorders keep only the allowed parameters.","type":"boolean"},"removed_query_parameters":{"description":"Query parameters whose values are removed while record_all_query_parameters is on, such as a token or an email address.","items":{"pattern":"^[A-Za-z0-9_.\\-\\[\\]]{1,64}$","type":"string"},"maxItems":50,"type":"array"},"sample_rate_percent":{"default":100,"maximum":100,"minimum":0,"type":"integer"},"unmask_selectors":{"items":{"maxLength":256,"type":"string"},"maxItems":100,"type":"array"}},"type":"object"}},"type":"object"},"SiteDetail":{"properties":{"config":{"$ref":"#/components/schemas/ConfigVersion"},"keys":{"description":"The keys currently accepted, newest first.","items":{"$ref":"#/components/schemas/SiteKey"},"type":"array"},"origins":{"items":{"$ref":"#/components/schemas/Origin"},"type":"array"},"site":{"$ref":"#/components/schemas/Site"}},"required":["site","config","origins","keys"],"type":"object"},"SiteKey":{"properties":{"created_at":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"},"key":{"description":"Public collection key; safe to publish.","pattern":"^ifa_site_[a-z2-7]{26}$","type":"string"},"retires_at":{"format":"date-time","type":"string"},"site_id":{"format":"uuid","type":"string"},"state":{"enum":["active","retiring"],"type":"string"}},"required":["id","site_id","key","state","created_at"],"type":"object"},"SiteListRow":{"allOf":[{"$ref":"#/components/schemas/Site"},{"properties":{"summary":{"description":"The site's setup state, counted for the list.","properties":{"analytics_enabled":{"type":"boolean"},"origins":{"type":"integer"},"recording_enabled":{"type":"boolean"},"verified_origins":{"type":"integer"}},"required":["origins","verified_origins","recording_enabled","analytics_enabled"],"type":"object"}},"required":["summary"],"type":"object"}]},"Size":{"properties":{"height":{"type":"integer"},"width":{"type":"integer"}},"required":["width","height"],"type":"object"},"TimelineItem":{"allOf":[{"$ref":"#/components/schemas/ReplayPoint"},{"properties":{"at":{"format":"date-time","type":"string"},"column":{"type":["integer","null"]},"duration_ms":{"description":"A request's time from its start, which is its at.","type":["integer","null"]},"id":{"type":"string"},"kind":{"description":"A page's kind; hidden or visible; an error's kind (error, rejection, console); a request's initiator (fetch, xhr, script, img, link, other); or why a recording ended.","type":["string","null"]},"line":{"type":["integer","null"]},"message":{"description":"An error's message, scrubbed of email addresses and long numbers.","type":["string","null"]},"name":{"description":"An error's name, a request's method or a custom event's name.","type":["string","null"]},"outcome":{"description":"A request's outcome. network_error: no answer, and the browser does not say why (offline, DNS, refused, CORS). opaque: a cross-origin answer whose status the browser hides. load_failed: a script, image or stylesheet that did not load; the browser does not say whether a server answered. aborted: the page stopped it, which is not a failure.","enum":["success","http_error","network_error","timeout","aborted","opaque","load_failed",null],"type":["string","null"]},"page_id":{"format":"uuid","type":["string","null"]},"path":{"description":"The page it happened on.","type":["string","null"]},"properties":{"additionalProperties":{"type":["string","number","boolean"]},"description":"A custom event's properties.","type":["object","null"]},"repeats":{"description":"How many times an error happened on its page.","type":["integer","null"]},"stack":{"description":"An error's stack: at most 12 frames, addresses without their query, scrubbed as messages are. Cleared with messages at the recording retention.","type":["string","null"]},"status":{"description":"A request's status, when the browser exposed one: only for success and http_error. Null otherwise; never a made-up 0.","type":["integer","null"]},"tab":{"type":"integer"},"type":{"enum":["page","visibility","error","request","custom","recording_started","recording_ended"],"type":"string"},"url":{"description":"A request's address or an error's source, under the site's query rules.","type":["string","null"]}},"required":["id","type","at","tab","page_id","path","kind","name","message","url","status","duration_ms","line","column","properties","outcome","stack","repeats"],"type":"object"}]},"Usage":{"properties":{"billing":{"description":"The analytics.recording meter of the organisation's billing account; null when this deployment does not bill yet.","oneOf":[{"$ref":"#/components/schemas/Meter"},{"type":"null"}]},"collection":{"properties":{"code":{"description":"The refusal code, as the collector answers a new recording: storage_limit, or the billing code; only with storage_limit and billing_paused.","type":"string"},"message":{"description":"What to do about it; do not parse.","type":"string"},"state":{"description":"recording when a new recording would be admitted now; paused when Ironfang paused the organisation's collection; storage_limit when its unexpired recordings hold max_stored_bytes, until expiry or deletion frees room; billing_paused when its billing account refuses new recordings (the free allowance is used without paid usage, a budget is reached, paid usage is paused, and so on). Recordings already open carry on.","enum":["recording","paused","storage_limit","billing_paused"],"type":"string"}},"required":["state"],"type":"object"},"max_open_recordings":{"description":"Recordings the organisation may have open at once; null for no limit of its own.","type":["integer","null"]},"max_sites":{"description":"Sites the organisation may have.","type":"integer"},"max_stored_bytes":{"description":"What the organisation's unexpired recordings may hold: once stored_bytes reaches it, no new recording is admitted (the collector answers 429 storage_limit) until expiry or deletion frees room. 10 GiB, unless a managed partner's agreement sets another figure; paying more does not lift it.","type":"integer"},"open_recordings":{"description":"Recordings admitted and not yet ended.","type":"integer"},"period_end":{"format":"date-time","type":"string"},"period_start":{"description":"The first instant of the UTC calendar month the figures are for.","format":"date-time","type":"string"},"recordings":{"description":"Recordings admitted in the month and kept: the billing count when billing is on.","type":"integer"},"stored_bytes":{"description":"What the organisation's unexpired recordings hold.","type":"integer"}},"required":["period_start","period_end","recordings","billing","collection","open_recordings","stored_bytes","max_stored_bytes","max_sites","max_open_recordings"],"type":"object"}},"securitySchemes":{"apiKey":{"bearerFormat":"if_live_ platform API key","description":"A platform API key minted in the portal. Its scopes are its\npermissions - `analytics:sites:read`, `analytics:sites:write` and the\nother `analytics:` scopes, or `analytics:*` - and it acts only in the\norganisation it was minted for. Keys without the scope a route needs\nare refused with 403.\n","scheme":"bearer","type":"http"},"bearerAuth":{"bearerFormat":"Warden JWT","description":"A Warden user token. Each route also requires a tenant permission in\nthe acting tenant (`X-Ironfang-Tenant`), resolved live from Warden,\nwhich each operation names: sites need `analytics.sites.read` to read\nand `analytics.sites.write` to change; sessions, recordings and\nheatmaps need `analytics.sessions.read`, and deleting a session or a\nrecording `analytics.sessions.delete`. A missing permission is 403; a\npermission lookup failure is 503.\n","scheme":"bearer","type":"http"}}},"externalDocs":{"description":"Ironfang Analytics documentation","url":"https://ironfang.com/analytics/docs"},"info":{"contact":{"email":"hello@ironfang.com","name":"Ironfang","url":"https://ironfang.com/contact"},"description":"Ironfang Analytics follows each visit to your website as a session:\nthe pages a visitor saw, in every tab, with how long they stayed, the\nerrors and failed requests they met, the events your pages tracked, and\nthe recordings that let you replay it, live or afterwards. This API\nmanages sites (the origins a recorder may send from, the public key its\ninstall snippet carries, and the capture configuration that decides\nwhat is collected and masked), searches and reads sessions and their\ntimelines, plays recordings back, and computes heatmaps.\nGET /v1/capabilities says what this deployment can do.\n\nA new site collects sessions as soon as its snippet runs and records\nnothing until recording is turned on. Anything that looks like a payment\ncard number is masked wherever it appears, and password, payment and\nauthentication fields are always blocked; no setting changes that.\nEvery change to a site's configuration creates a new immutable version,\nso a recording can always name the rules it was captured under.\n\nEverything is organisation-bound: an object in another organisation is\nreported as not found. Authenticate with an Ironfang platform API key\ncarrying Analytics scopes, or a Warden user token. Base URL\nhttps://api.ironfang.com/analytics.\n\nFor agents and tools: every operation has a stable `operationId`, and\nevery error is JSON of the shape\n`{\"error\": {\"code\", \"message\", \"docs\"}, \"request_id\"}`: branch on `code`\nand the HTTP status, never on `message`.\n","license":{"name":"Proprietary - use governed by the Ironfang terms of service","url":"https://ironfang.com/legal/terms"},"summary":"Sites, origins, public keys and capture settings for session replay and web analytics.","termsOfService":"https://ironfang.com/legal/terms","title":"Ironfang Analytics API","version":"0.1.0"},"openapi":"3.1.0","paths":{"/v1/capabilities":{"get":{"description":"Which features this deployment offers today (a planned feature is listed as false rather than left out), the limits it applies and the schema versions it speaks. Needs analytics.sites.read.","operationId":"getCapabilities","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Capabilities"}}},"description":"Capabilities"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Get capabilities and limits","tags":["Capabilities"]}},"/v1/recordings":{"get":{"description":"Returns the organisation's unexpired recordings across its sites, newest first, as a bounded keyset page. Every filter is optional and they combine. Needs analytics.sessions.read.","operationId":"listRecordings","parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"site_id","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"Created at or after this time.","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"Created before this time.","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"One state, or several separated by commas.","in":"query","name":"state","required":false,"schema":{"example":"ready,partial","type":"string"}},{"description":"Two-letter country code, upper case.","in":"query","name":"country","required":false,"schema":{"pattern":"^[A-Z]{2}$","type":"string"}},{"in":"query","name":"device","required":false,"schema":{"example":"mobile","type":"string"}},{"in":"query","name":"browser","required":false,"schema":{"example":"chrome","type":"string"}},{"in":"query","name":"os","required":false,"schema":{"example":"windows","type":"string"}},{"description":"One address, or a network in CIDR notation.","in":"query","name":"ip","required":false,"schema":{"example":"203.0.113.0/24","type":"string"}},{"description":"Entry paths starting with this.","in":"query","name":"entry_path","required":false,"schema":{"example":"/apply","type":"string"}},{"description":"Recordings that showed this page at any point, by its path without a query string or fragment. Pages are indexed when a recording is processed for heatmaps, shortly after it ends; until then it does not match.","in":"query","name":"visited_path","required":false,"schema":{"example":"/pricing","type":"string"}},{"description":"One browser's recordings, by the visitor id on any of them.","in":"query","name":"visitor_id","required":false,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"next_cursor":{"description":"Opaque cursor for the next page; empty on the last page.","type":"string"},"recordings":{"items":{"$ref":"#/components/schemas/RecordingRow"},"type":"array"}},"required":["recordings","next_cursor"],"type":"object"}}},"description":"A page of recordings"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Search recordings","tags":["Recordings"]}},"/v1/recordings/{recordingId}":{"delete":{"description":"Deletes the recording's stored batches, then the recording with its IP address, user agent and every other detail. A recording deleted before it ended is not counted; one already counted stays counted. Needs analytics.sessions.delete.","operationId":"deleteRecording","responses":{"204":{"description":"Recording deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment cannot delete recordings yet (`deletion_unavailable`)."}},"summary":"Delete a recording","tags":["Recordings"]},"get":{"description":"One recording with everything kept about it and its epochs. Needs analytics.sessions.read.","operationId":"getRecording","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingDetail"}}},"description":"The recording"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get a recording","tags":["Recordings"]},"parameters":[{"$ref":"#/components/parameters/RecordingId"}]},"/v1/recordings/{recordingId}/chunks/{epoch}/{sequence}":{"get":{"description":"The batch's rrweb events as a JSON array, masked in the browser before they were sent. A gzip batch is served with Content-Encoding: gzip. The bytes are checked against the hash recorded when the batch was accepted. Needs analytics.sessions.read.","operationId":"getChunk","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"The events"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment cannot play recordings yet (`playback_unavailable`)."}},"summary":"Get one batch of events","tags":["Recordings"]},"parameters":[{"$ref":"#/components/parameters/RecordingId"},{"in":"path","name":"epoch","required":true,"schema":{"minimum":0,"type":"integer"}},{"in":"path","name":"sequence","required":true,"schema":{"minimum":0,"type":"integer"}}]},"/v1/recordings/{recordingId}/playback":{"get":{"description":"The committed batches that can be played, in order: in each epoch, those before its first gap. Fetch each from the chunk endpoint. A live viewer passes `after`, the last batch it has, and gets only the batches since. `now` and `live` let a live viewer say how far behind the visitor it is (see LivePlayback). Needs analytics.sessions.read.","operationId":"getPlayback","parameters":[{"description":"Only batches after this one, as `\u003cepoch\u003e:\u003csequence\u003e`.","in":"query","name":"after","required":false,"schema":{"example":"0:12","pattern":"^[0-9]+:[0-9]+$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"chunks":{"items":{"$ref":"#/components/schemas/PlaybackChunk"},"type":"array"},"live":{"$ref":"#/components/schemas/LivePlayback"},"now":{"description":"This server's clock when it took the request. A viewer bounds its own clock against it by the round trip.","format":"date-time","type":"string"}},"required":["chunks","now","live"],"type":"object"}}},"description":"The batches"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"List a recording's playable batches","tags":["Recordings"]},"parameters":[{"$ref":"#/components/parameters/RecordingId"}]},"/v1/recordings/{recordingId}/watch":{"parameters":[{"$ref":"#/components/parameters/RecordingId"}],"post":{"description":"Marks an open recording as watched for 30 seconds from now; renew it while the recording is on screen. While the lease holds, the collector tells the visitor's recorder to upload every second instead of every ten, so new batches reach `playback` within a few seconds. That is all it changes: the visitor's page shows nothing and behaves no differently. Needs analytics.sessions.read.","operationId":"watchRecording","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"watched_until":{"format":"date-time","type":"string"}},"required":["watched_until"],"type":"object"}}},"description":"The lease"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The recording has ended (`recording_ended`); replay it instead."}},"summary":"Take or renew the live-view lease","tags":["Recordings"]}},"/v1/sessions":{"get":{"description":"Returns the organisation's sessions across its sites, newest first by when they started, as a bounded keyset page. Every filter is optional and they combine. Needs analytics.sessions.read.","operationId":"listSessions","parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"in":"query","name":"site_id","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"Started at or after this time.","in":"query","name":"from","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"Started before this time.","in":"query","name":"to","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"live (heard from in the last 90 seconds), active (not ended) or ended.","in":"query","name":"state","required":false,"schema":{"enum":["live","active","ended"],"type":"string"}},{"description":"Two-letter country code, upper case.","in":"query","name":"country","required":false,"schema":{"pattern":"^[A-Z]{2}$","type":"string"}},{"in":"query","name":"device","required":false,"schema":{"example":"mobile","type":"string"}},{"in":"query","name":"browser","required":false,"schema":{"example":"chrome","type":"string"}},{"in":"query","name":"os","required":false,"schema":{"example":"windows","type":"string"}},{"description":"One address, or a network in CIDR notation.","in":"query","name":"ip","required":false,"schema":{"example":"203.0.113.0/24","type":"string"}},{"description":"Entry paths starting with this.","in":"query","name":"entry_path","required":false,"schema":{"example":"/apply","type":"string"}},{"description":"Sessions that visited this page, by its path without a query string or fragment.","in":"query","name":"visited_path","required":false,"schema":{"example":"/pricing","type":"string"}},{"description":"One browser's sessions.","in":"query","name":"visitor_id","required":false,"schema":{"format":"uuid","type":"string"}},{"description":"with, sessions with a recording that can be played; without, the rest.","in":"query","name":"recording","required":false,"schema":{"enum":["with","without"],"type":"string"}},{"description":"Sessions with errors (uncaught, unhandled rejections or console errors), with failed requests (an error answer, no answer, a timeout or a resource that did not load; not an aborted one), or with either (any).","in":"query","name":"diagnostics","required":false,"schema":{"enum":["errors","requests","any"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"next_cursor":{"description":"Opaque cursor for the next page; empty on the last page.","type":"string"},"sessions":{"items":{"$ref":"#/components/schemas/SessionRow"},"type":"array"}},"required":["sessions","next_cursor"],"type":"object"}}},"description":"A page of sessions"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Search sessions","tags":["Sessions"]}},"/v1/sessions/{sessionId}":{"delete":{"description":"Erases the session: its recordings' stored batches, then the session with its pages, events, recordings, IP address, user agent and every other detail. Recordings already counted stay counted. Needs analytics.sessions.delete.","operationId":"deleteSession","responses":{"204":{"description":"Session deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment cannot delete sessions yet (`deletion_unavailable`)."}},"summary":"Delete a session","tags":["Sessions"]},"get":{"description":"One session with what its browser reported, its journey (every page in the order the visitor saw them, with how long they stayed) and its recordings, each page placed in the recording that saw it. Needs analytics.sessions.read.","operationId":"getSession","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionDetail"}}},"description":"The session"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get a session","tags":["Sessions"]},"parameters":[{"$ref":"#/components/parameters/SessionId"}]},"/v1/sessions/{sessionId}/events":{"get":{"description":"Everything that happened in the session: its pages, events, and recordings starting and ending, in the order they happened by the browser's own clock (which every tab of a session shares), then each tab's own order; never in the order they arrived. A session holds at most 300 pages and 1,000 events, so the timeline comes whole. Needs analytics.sessions.read.","operationId":"getSessionEvents","parameters":[{"description":"A comma list of page, visibility, error, request, custom and recording.","in":"query","name":"type","required":false,"schema":{"example":"error,request","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"events":{"items":{"$ref":"#/components/schemas/TimelineItem"},"type":"array"}},"required":["events"],"type":"object"}}},"description":"The timeline"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get a session's timeline","tags":["Sessions"]},"parameters":[{"$ref":"#/components/parameters/SessionId"}]},"/v1/sessions/{sessionId}/heatmap":{"get":{"description":"Attention, scroll reach, clicks and movement on one page from this session's visits alone, every tab's, drawn over its own recording; the same figures as a site's heatmap. Heatmaps come from recordings, extracted once each ends, so a page the session did not record, or whose recording is still open, expired or deleted, has none. Needs analytics.sessions.read.","operationId":"getSessionHeatmap","parameters":[{"description":"The page; without one, the first page the session came to.","in":"query","name":"path","required":false,"schema":{"example":"/pricing","type":"string"}},{"description":"The device; without one, the one the session saw the page on most.","in":"query","name":"bucket","required":false,"schema":{"$ref":"#/components/schemas/HeatmapBucket"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionHeatmap"}}},"description":"The session's heatmap"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get a session's heatmap","tags":["Sessions"]},"parameters":[{"$ref":"#/components/parameters/SessionId"}]},"/v1/sites":{"get":{"description":"Returns the organisation's sites, newest first, as a bounded keyset page. Each row carries the counts a setup state is read from, so a page of sites is one request. `total` is the organisation's sites, independent of the page. With `external_id`, only the site that has it: the exact lookup an integration uses to find a site it created. Needs analytics.sites.read.","operationId":"listSites","parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"},{"description":"Only the site with this external id, within the organisation.","in":"query","name":"external_id","required":false,"schema":{"pattern":"^[A-Za-z0-9._:-]{1,128}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"next_cursor":{"description":"Opaque cursor for the next page; empty on the last page.","type":"string"},"sites":{"items":{"$ref":"#/components/schemas/SiteListRow"},"type":"array"},"total":{"description":"How many sites the organisation has.","type":"integer"}},"required":["sites","next_cursor","total"],"type":"object"}}},"description":"A page of sites"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"List sites","tags":["Sites"]},"post":{"description":"Creates a site with its first configuration version and one active public key. Origins are optional here and can be added later. A configuration that is left out, or names only some fields, takes the privacy-first defaults for the rest; recording is not switched on by creating a site. With an `Idempotency-Key`, a retry of the same request returns the first answer (with `Idempotent-Replayed: true`) and creates nothing; the key is honoured for 24 hours. A partner integration's managed key must send one. `external_id` is your own identifier: unique in the organisation and fixed, it finds the site with `GET /v1/sites?external_id=` even after the key has expired. Needs analytics.sites.write.","operationId":"createSite","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"config":{"$ref":"#/components/schemas/SiteConfig"},"external_id":{"description":"Your own identifier for the site. Opaque to Ironfang, not a secret, and never changes.","example":"checkout-1042","pattern":"^[A-Za-z0-9._:-]{1,128}$","type":"string"},"name":{"example":"Checkout","maxLength":120,"type":"string"},"origins":{"items":{"example":"https://shop.example.com","type":"string"},"maxItems":20,"type":"array"},"retention_days":{"default":28,"maximum":28,"minimum":1,"type":"integer"},"session_retention_days":{"default":90,"description":"How long session history is kept; at least retention_days.","maximum":90,"minimum":1,"type":"integer"},"timezone":{"default":"UTC","description":"IANA time zone used for reports.","example":"Europe/London","type":"string"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteDetail"}}},"description":"Site created, or the stored answer to the same request with the same Idempotency-Key","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}}},"400":{"$ref":"#/components/responses/InvalidIdempotency"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"properties":{"site_id":{"description":"The site that has the external_id, for external_id_conflict.","format":"uuid","type":"string"}},"type":"object"}]}}},"description":"An origin is already another site's (`conflict`); the Idempotency-Key was used for a different request (`idempotency_conflict`); or another site already has this external_id (`external_id_conflict`, whose `site_id` names it)."},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/RateLimited"}},"summary":"Create a site","tags":["Sites"]}},"/v1/sites/{siteId}":{"get":{"description":"The site with its configuration in force, its origins (with the DNS record each still needs) and the public keys currently accepted. Needs analytics.sites.read.","operationId":"getSite","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteDetail"}}},"description":"The site"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get a site","tags":["Sites"]},"parameters":[{"$ref":"#/components/parameters/SiteId"}],"patch":{"description":"Changes a site, made against the version you read: if the site has changed since, the request is refused with 409 `version_conflict` and nothing is applied. `config` merges onto the configuration in force: a field left out keeps its value and a list that is named replaces the whole list. A configuration that differs from the one in force becomes a new immutable version. Setting `status` to `disabled` stops collection without deleting anything. Needs analytics.sites.write.","operationId":"updateSite","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"config":{"$ref":"#/components/schemas/SiteConfig"},"name":{"maxLength":120,"type":"string"},"retention_days":{"description":"Raising it above session_retention_days raises that too.","maximum":28,"minimum":1,"type":"integer"},"session_retention_days":{"description":"At least retention_days.","maximum":90,"minimum":1,"type":"integer"},"status":{"enum":["active","disabled"],"type":"string"},"timezone":{"type":"string"},"version":{"description":"The site version the change was made against.","minimum":1,"type":"integer"}},"required":["version"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteDetail"}}},"description":"The site after the change"},"400":{"$ref":"#/components/responses/InvalidJSON"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/InvalidRequest"}},"summary":"Change a site","tags":["Sites"]}},"/v1/sites/{siteId}/config-versions":{"get":{"description":"Every configuration version the site has had, newest first, as a bounded keyset page, with who made each one. Versions are never edited. Needs analytics.sites.read.","operationId":"listConfigVersions","parameters":[{"$ref":"#/components/parameters/Cursor"},{"$ref":"#/components/parameters/Limit"}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"config_versions":{"items":{"$ref":"#/components/schemas/ConfigVersion"},"type":"array"},"next_cursor":{"description":"Opaque cursor for the next page; empty on the last page.","type":"string"}},"required":["config_versions","next_cursor"],"type":"object"}}},"description":"A page of configuration versions"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"List a site's configuration history","tags":["Sites"]},"parameters":[{"$ref":"#/components/parameters/SiteId"}]},"/v1/sites/{siteId}/heatmaps":{"get":{"description":"Attention, scroll reach, clicks and movement for one page and device over a period, from at most 20,000 of its visits (a deterministic sample when there are more), with the visit to draw it over. Fewer than 30 visits is flagged small_sample. Needs analytics.sessions.read.","operationId":"getHeatmap","parameters":[{"$ref":"#/components/parameters/HeatmapPath"},{"$ref":"#/components/parameters/HeatmapBucketParam"},{"$ref":"#/components/parameters/HeatmapFrom"},{"$ref":"#/components/parameters/HeatmapTo"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Heatmap"}}},"description":"Heatmap"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get a page's heatmap","tags":["Heatmaps"]},"parameters":[{"$ref":"#/components/parameters/SiteId"}]},"/v1/sites/{siteId}/heatmaps/compare":{"get":{"description":"One page and device over two periods, with the targets clicked in both and how their rate changed. overlay_valid says both periods have the same most common layout, so a difference overlay on one page means something; across a redesign, show them side by side. Needs analytics.sessions.read.","operationId":"compareHeatmaps","parameters":[{"$ref":"#/components/parameters/HeatmapPath"},{"$ref":"#/components/parameters/HeatmapBucketParam"},{"in":"query","name":"a_from","required":true,"schema":{"format":"date-time","type":"string"}},{"in":"query","name":"a_to","required":true,"schema":{"format":"date-time","type":"string"}},{"in":"query","name":"b_from","required":true,"schema":{"format":"date-time","type":"string"}},{"in":"query","name":"b_to","required":true,"schema":{"format":"date-time","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"a":{"$ref":"#/components/schemas/Heatmap"},"b":{"$ref":"#/components/schemas/Heatmap"},"elements":{"items":{"properties":{"a":{"description":"Clicks per visit in the first period.","type":"number"},"b":{"description":"Clicks per visit in the second period.","type":"number"},"change":{"type":"number"},"selector":{"type":"string"}},"required":["selector","a","b","change"],"type":"object"},"type":"array"},"overlay_valid":{"type":"boolean"}},"required":["a","b","overlay_valid","elements"],"type":"object"}}},"description":"Comparison"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Compare two periods","tags":["Heatmaps"]},"parameters":[{"$ref":"#/components/parameters/SiteId"}]},"/v1/sites/{siteId}/installation":{"get":{"description":"What must be true before the site records, step by step, and the public key its snippet will carry. `snippet` is the script tag that installs the current SDK release, pinned by integrity; it is null only when no release or active key is available. Needs analytics.sites.read.","operationId":"getInstallation","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Installation"}}},"description":"Installation status"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Get installation status","tags":["Sites"]},"parameters":[{"$ref":"#/components/parameters/SiteId"}]},"/v1/sites/{siteId}/keys/rotate":{"parameters":[{"$ref":"#/components/parameters/SiteId"}],"post":{"description":"Mints a new public collection key. The key it replaces stays accepted for 24 hours so pages cached with the old snippet keep working; a key that was already retiring is revoked, so at most two keys are ever accepted. Public keys are not secrets and can only ever ask to record for their own site. With an `Idempotency-Key`, a retry rotates once and returns the first answer; a partner integration's managed key must send one. Needs analytics.sites.write.","operationId":"rotateSiteKey","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteDetail"}}},"description":"The site with its keys after rotation, or the stored answer to a retry","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}}},"400":{"$ref":"#/components/responses/InvalidIdempotency"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The Idempotency-Key was used for a different request (`idempotency_conflict`)."},"429":{"$ref":"#/components/responses/RateLimited"}},"summary":"Rotate the public key","tags":["Keys"]}},"/v1/sites/{siteId}/origins":{"parameters":[{"$ref":"#/components/parameters/SiteId"}],"post":{"description":"Adds an origin the site's recorder may send from. It is normalised to exactly what a browser sends (lower case, no default port, no path). HTTPS is required except on localhost and loopback addresses, which are for development and need no verification. A public origin stays pending until its DNS TXT record is verified. Needs analytics.sites.write.","operationId":"addOrigin","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"origin":{"example":"https://shop.example.com","type":"string"}},"required":["origin"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"origin":{"$ref":"#/components/schemas/Origin"}},"required":["origin"],"type":"object"}}},"description":"Origin added"},"400":{"$ref":"#/components/responses/InvalidJSON"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/InvalidRequest"}},"summary":"Add an origin","tags":["Origins"]}},"/v1/sites/{siteId}/origins/{originId}":{"delete":{"description":"Removes an origin; recorders on it are refused from then on. Needs analytics.sites.write.","operationId":"removeOrigin","responses":{"204":{"description":"Origin removed"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Remove an origin","tags":["Origins"]},"parameters":[{"$ref":"#/components/parameters/SiteId"},{"$ref":"#/components/parameters/OriginId"}]},"/v1/sites/{siteId}/origins/{originId}/verify":{"parameters":[{"$ref":"#/components/parameters/SiteId"},{"$ref":"#/components/parameters/OriginId"}],"post":{"description":"Looks up the origin's TXT record (`dns_record` on the origin) once. If a record has exactly the expected value the origin is verified for good; a later failed lookup never un-verifies it. Not finding the record is a normal answer (200 with `found` false), because DNS changes take time to appear. Needs analytics.sites.write.","operationId":"verifyOrigin","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"detail":{"description":"Why the record was not found, when it was not.","type":"string"},"found":{"type":"boolean"},"origin":{"$ref":"#/components/schemas/Origin"}},"required":["origin","found"],"type":"object"}}},"description":"The result of the check"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"Check an origin's DNS record","tags":["Origins"]}},"/v1/sites/{siteId}/pages":{"get":{"description":"The site's pages that recorded visits showed in a period, most visited first, by device. A recording is indexed shortly after it ends. Needs analytics.sessions.read.","operationId":"listHeatmapPages","parameters":[{"$ref":"#/components/parameters/HeatmapFrom"},{"$ref":"#/components/parameters/HeatmapTo"},{"in":"query","name":"limit","required":false,"schema":{"default":100,"maximum":500,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"from":{"format":"date-time","type":"string"},"pages":{"items":{"$ref":"#/components/schemas/HeatmapPage"},"type":"array"},"to":{"format":"date-time","type":"string"}},"required":["from","to","pages"],"type":"object"}}},"description":"Pages"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"List pages with visits","tags":["Heatmaps"]},"parameters":[{"$ref":"#/components/parameters/SiteId"}]},"/v1/sites/{siteId}/pages/changes":{"get":{"description":"Each day whose most common layout differs from the previous day the page was visited: when the page changed, for comparing the week before with the week after. Days are in tz, or the site's time zone. Needs analytics.sessions.read.","operationId":"listLayoutChanges","parameters":[{"$ref":"#/components/parameters/HeatmapPath"},{"description":"One device; every device when left out.","in":"query","name":"bucket","required":false,"schema":{"$ref":"#/components/schemas/HeatmapBucket"}},{"$ref":"#/components/parameters/HeatmapFrom"},{"$ref":"#/components/parameters/HeatmapTo"},{"description":"An IANA time zone for the days.","in":"query","name":"tz","required":false,"schema":{"example":"Europe/London","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"changes":{"items":{"properties":{"day":{"format":"date","type":"string"},"from":{"type":["string","null"]},"to":{"type":["string","null"]}},"required":["day","from","to"],"type":"object"},"type":"array"},"path":{"type":"string"},"timezone":{"type":"string"}},"required":["path","timezone","changes"],"type":"object"}}},"description":"Layout changes"},"400":{"$ref":"#/components/responses/InvalidQuery"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"summary":"List the days a page's layout changed","tags":["Heatmaps"]},"parameters":[{"$ref":"#/components/parameters/SiteId"}]},"/v1/usage":{"get":{"description":"This calendar month's (UTC) recordings: the analytics.recording meter of the organisation's billing account (its free allowance, what is used, held and charged), whether a new recording would be admitted now and, if not, whether Ironfang paused it, the organisation holds its stored-bytes limit or billing refuses it, and the organisation's capacity: sites, open recordings, and what it stores against what it may. A recording belongs to the month it was admitted in. Needs analytics.usage.read.","operationId":"getUsage","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Usage"}}},"description":"This month's usage"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"summary":"Get this month's usage","tags":["Usage"]}}},"security":[{"apiKey":[]},{"bearerAuth":[]}],"servers":[{"url":"https://api.ironfang.com/analytics"}],"tags":[{"description":"What this deployment can do and the limits it applies.","name":"Capabilities"},{"description":"Instrumented websites and their capture configuration.","name":"Sites"},{"description":"The origins a site's recorder may send from and their DNS verification.","name":"Origins"},{"description":"The public collection key a site's install snippet carries.","name":"Keys"},{"description":"One browser's visit to one site: every tab, from the first page until thirty minutes pass without the visitor doing anything, or twelve hours from its start. A session holds its pages, its events and its recordings, and stays readable when recording is off or its recordings have expired. Sessions keep the visitor's IP address, user agent and visitor id for the site's recording retention, then lose them, and the rest for its session retention.","name":"Sessions"},{"description":"Recordings of one tab within a session: search, what the visitor's browser reported, the batches to play them back from, live viewing and deletion. Every recording keeps the visitor's IP address and full user agent until it expires or is deleted; its session then keeps a note that it was there.","name":"Recordings"},{"description":"Totals of where visitors click, how far they scroll, how long each part of a page is on screen and where the pointer goes, across the recorded visits to one page. Computed from recordings, so they keep what recordings keep and need analytics.sessions.read. Every figure is per page visit; horizontal positions count from the centre of the viewport.","name":"Heatmaps"},{"description":"This month's recordings as the organisation's billing account counts them, and whether new recordings are being admitted. Ironfang Analytics has no plans of its own: every Ironfang account has one billing account, with a free monthly allowance of recordings and pay-as-you-go usage beyond it, managed with every other product's usage at https://api.ironfang.com/billing/v1. A recording is counted once, when it ends kept: it became playable and was not discarded as untouched, empty or too short. The former /v1/billing routes answer 410 `billing_moved`.","name":"Usage"}]}