Skip to main content
If you already track purchase, upgrade, signup, churn, or any other product event in Amplitude, you can pipe those events into Greenflash with one destination — no SDKs, no app code. Conversation analyses immediately get a downstream-outcome layer: which conversations led to revenue, which led to refunds, which churned silently.

How it works

Amplitude has built-in Event Streaming destinations that POST the Amplitude HTTP V2 batch format ({ events: [...] }) to a URL of your choice. Greenflash gives you a per-product URL to point it at. The flow:
  1. Your app sends an event to Amplitude (amplitude.track('purchase', { revenue: 49 }) or equivalent).
  2. Amplitude evaluates your destination’s event filters and decides whether to forward.
  3. Amplitude POSTs a batch of events (up to 1,000) to your Greenflash URL.
  4. Greenflash authenticates the URL token, maps each event in the batch to a Greenflash business event, infers positive / negative / neutral influence, and attaches it to the product you connected.
  5. The next conversation analysis Greenflash runs has those events as outcome context.
Your Amplitude instrumentation stays the source of truth. Greenflash sees a copy of the events your destinations already see.

Before you start

  • A Greenflash product to enrich with Amplitude events. Create one under Settings → Products if you don’t have one.
  • Amplitude org admin or destination admin access so you can create an event streaming destination.
  • (Optional) A list of the events worth forwarding. Forwarding everything works but is rarely what you want — page views and exposure events are noise for Greenflash.

Setup

1. Create the connection in Greenflash

  1. Open Settings → Connect → Events in the Greenflash web app.
  2. Click Connect on the Amplitude card.
  3. Pick the product the stream should feed and click Create source.
  4. Copy the destination URL. Greenflash shows it once. If you lose it, rotate the token from the connection row and update Amplitude.
The URL is the secret. Store it in Amplitude and your secrets manager — anyone with the URL can write events into the connected product.

2. Add the destination in Amplitude

  1. In Amplitude, open Data → Destinations and click Add Destination.
  2. Choose Event Stream (HTTP API destination) — the one that posts JSON over HTTP.
  3. Set the destination URL to the Greenflash URL. Use POST and Content-Type: application/json.
  4. Under the destination’s Event filters, select which events should stream. Allow only your business events (signup, activate, purchase, subscribe, refund, cancel) — Amplitude defaults to forwarding everything, so this is where you control volume and quality.
  5. Save the destination. The connection row in Greenflash flips to Active on the first event.
Greenflash ignores the api_key in Amplitude’s request body — your URL token is the only auth. You can leave Amplitude’s destination key field empty or set it to any placeholder.

3. Send a test event

Trigger a real event from your app, replay one from Amplitude’s destination logs, or post the HTTP V2 batch shape directly to confirm the round trip:
curl -X POST "<YOUR_GREENFLASH_URL>" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "user_id": "user_test_123",
        "event_type": "purchase",
        "time": 1747569600000,
        "insert_id": "amp_test_001",
        "event_properties": { "plan": "pro" },
        "revenue": 49.0
      }
    ]
  }'
A successful request returns:
{ "received": 1, "accepted": 1, "dropped": 0 }
If received is 0, the payload didn’t match the HTTP V2 shape — check that you’re using { events: [...] } with each event containing event_type.

How events are mapped

Greenflash applies these defaults to every Amplitude event:
Amplitude fieldGreenflash field
event_typeeventType
user_id (falls back to device_id)externalUserId
time (ms since epoch)eventAt
insert_idinsertId (prefixed amplitude:) — used for idempotency
revenue (or price * quantity if no revenue)value (with valueType: currency)
event_properties + revenue fields (revenue, price, quantity, revenueType)properties

Identity hints (used for user & organization attribution)

Greenflash reads a handful of fields from user_properties and groups to attach the event to the right user and organization. Sending these means Greenflash can auto-create the user / org if it hasn’t seen them yet, instead of leaving the event unattached.
Amplitude fieldGreenflash field
user_properties.emailuserHints.email
user_properties.first_name + user_properties.last_name (or user_properties.name)userHints.name
groups.organization or groups.company (or user_properties.organization_id)externalOrganizationId
user_properties.organization_name (or user_properties.company)organizationHints.name

Attribution: how events tie to users, orgs, and conversations

Users. Events match an existing Greenflash user by externalUserId first, then by email. If no match exists and the event carries an email, Greenflash upserts a new user automatically using the user_properties above. Anonymous events (no email) keep userId = null rather than creating a row per device. Organizations. Same idea, scoped to externalOrganizationId. Org upsert only runs when groups.organization (or equivalent) is present — never by name. Conversations. Add gf_conversation_id to event_properties to attach the event to a specific Greenflash conversation. Otherwise, the event auto-attaches to the user’s most-recent conversation in this product within the last 15 minutes; if that doesn’t match, conversationId stays null.
Users and orgs auto-created from analytics carry properties.created_from = 'analytics' and properties.source = 'amplitude'. When you later identify them via the Greenflash SDK using the same externalUserId, the existing row is reused.

Influence (positive / negative / neutral)

  • Positive when revenue > 0 (or price * quantity > 0), or the event name matches signup, signed_up, upgrade, purchase, subscribe, convert, activate, complete, or order_completed.
  • Negative when revenue < 0 (Amplitude’s refund convention), or the event name matches churn, cancel, unsubscribe, refund, downgrade, delete, failed, or error.
  • Neutral otherwise.
Matching is case-insensitive and works across snake_case, kebab-case, PascalCase, and Title Case With Spaces.
Refunds in Amplitude are conventionally a normal event with revenueType: "Refund" and a negative revenue number. Make sure your destination forwards the top-level revenue field, not just event_properties — otherwise Greenflash can’t infer the negative dollar value.

Override the defaults

Add any of these magic properties inside the event’s event_properties and Greenflash will respect them — no Greenflash-side configuration needed.
PropertyEffect
gf_event_typeOverride the Greenflash event name (the original Amplitude name stays in properties)
gf_influenceForce positive, negative, or neutral
gf_quality_impact_scoreNumeric override from -1.0 to 1.0 — fine-grained weight
gf_valueOverride the event value (string or number)
gf_value_typeOne of currency, numeric, text, boolean
gf_conversation_idAttach this event to a specific Greenflash conversation (externalConversationId)
Example: forward a custom milestone but tell Greenflash to treat it as a strongly positive outcome.
amplitude.track('weekly_active_milestone', {
  weeks: 4,
  gf_influence: 'positive',
  gf_quality_impact_score: 0.8,
});

Filtering: what to forward

Filter on the Amplitude side, not inside Greenflash. Amplitude’s destination Event filter is more expressive than anything we’d reasonably build — property comparisons, segment-style conditions, transformations. We deliberately don’t expose a denylist UI inside Greenflash: the only Greenflash-side knob is the magic-property overrides above, applied per event in event_properties. The fewer noisy events you forward, the sharper your Greenflash analyses get. Use Amplitude’s destination filter to allow only the events that represent real outcomes (signup, activate, purchase, subscribe, refund, cancel) — Amplitude defaults to forwarding everything, which usually isn’t what you want.
Forward signup, trial_started, activated, subscription_started, subscription_upgraded, subscription_cancelled. Greenflash infers influence from these names automatically.
Forward purchase and refund (or your equivalents). Always include the top-level revenue field so Greenflash can dollar-weight outcomes per conversation. Use a negative revenue with revenueType: "Refund" for refunds.
Forward activation milestones (workspace_created, integration_connected, aha_moment_reached). Add gf_influence: 'positive' if the event name doesn’t already match Greenflash’s defaults.
Don’t want to edit Amplitude’s filters for a short outage or experiment? Pause the connection from its row in Settings → Connect → Events. The Greenflash receiver returns 204 while paused so Amplitude stops retrying. Re-enable to resume — no token rotation, no Amplitude change.

Response codes & limits

CodeMeaning
200Batch received and processed. Body includes received, accepted, dropped.
204Connection is paused or disconnected in Greenflash. Returned deliberately so Amplitude stops retrying.
400Payload didn’t match the Amplitude HTTP V2 schema, or JSON was invalid.
401URL token is invalid, the connection has been rotated, or the optional shared secret header is missing / wrong.
404Provider segment of the URL is wrong (e.g. you pointed Amplitude at the /posthog/ URL).
413More than 1,000 events in a single batch. The whole batch is dropped — configure Amplitude to split larger batches.
500Greenflash couldn’t look up the connection. Safe to retry.

Security

The URL is path-authenticated: the token segment is generated per connection, stored only as a scrypt hash, and verified in constant time on every request. The api_key in Amplitude’s request body is ignored. For defense in depth, you can configure a shared secret alongside the URL token. The secret is communicated to Greenflash on every request via a custom HTTP header — there’s no body field or query parameter.
  1. In Amplitude, open your destination and go to Custom Headers.
  2. Add a header with name x-greenflash-secret and value <your-secret> (any string you pick, 8+ characters).
  3. Provide the same secret to Greenflash on the connection so we can match it.
Once configured, Greenflash rejects any request whose x-greenflash-secret header is missing or doesn’t match with 401. Rotating the URL invalidates the previous token immediately. Events sent to the old URL return 401. Update the URL on the Amplitude destination to resume delivery.

Troubleshooting

  • The destination is enabled and your events match its filter.
  • The URL has no trailing slash.
  • You’re using the Event Stream (HTTP API) destination type, not a managed integration that may transform the payload to a non-V2 shape.
  • Check Amplitude’s destination logs for non-2xx responses to see what Greenflash returned.
Amplitude refunds use revenueType: "Refund" with a negative revenue number. Make sure your destination’s payload includes the top-level revenue field — Greenflash reads it from there, not from event_properties.
The batch contained more than 1,000 events. Greenflash drops the entire batch above that cap. Reduce the destination’s batch size in Amplitude.
Amplitude events without user_id fall back to device_id for the Greenflash externalUserId. If neither is set, the event arrives unattributed. Make sure your destination forwards both fields.
Open the Amplitude row under Settings → Connect → Events and click Rotate URL. The old token is invalidated immediately. Paste the new URL into the Amplitude destination.
Greenflash deduplicates on the amplitude:<insert_id> insert id. Amplitude SDKs set insert_id automatically; if you’re posting events directly to the URL, include a stable insert_id per event.

Next

How Greenflash uses events

What changes in your analyses once events start flowing.

Public Events API

Skip the webhook and post events directly if you want full control.