Ingestion API
One endpoint. Send each completed transaction and CounterMeasure handles the rest: hashing, filtering, deduplication, retries, and delivery to the store’s Meta dataset as a Purchase · Physical store event.
Endpoint
POST https://countermeasure.bigrush.com/v1/ingest Authorization: Bearer cm_live_… Content-Type: application/json
Each API key belongs to exactly one store and can only write to it. Store owners create and rotate keys on the store page of the dashboard. Up to 1,000 transactions per request.
Transaction fields
| Field | Required | Notes |
|---|---|---|
| order_id | Yes | Unique per store. Sending the same id again is safe: it’s counted once. |
| occurred_at | Yes | ISO 8601. Include the UTC offset; without one, the store’s time zone is assumed. |
| total | — | Sale value in the store’s currency. Rows under $0.01 are skipped by default. |
| — | Raw or already-normalized. Hashed (SHA-256) the moment it arrives. | |
| phone | — | Any format. Normalized to digits with country code, then hashed. |
| first_name / last_name | — | Optional. Hashed; improves Meta’s match rate. |
| type | — | Transaction type (e.g. sale, buy). Stores can choose which types to send. |
| store_id | — | Optional. If present, it must match the store the key belongs to. |
At least one of email or phone is needed; a sale Meta can’t match to a person is skipped.
Response
HTTP/1.1 202 Accepted
{
"store": "204",
"received": 3,
"accepted": 1,
"duplicates": 1,
"skipped": { "low_value": 1 }
}Skip reasons: low_value, too_old (older than the store’s freshness window, 5 days by default; Meta rejects anything over 7), no_customer_match_keys, no_order_id, bad_timestamp, excluded_type. Errors: 401 invalid_api_key, 422 invalid_body / store_mismatch.
Example: Ruby
require "net/http"
require "json"
# One key per store, from the CounterMeasure dashboard.
uri = URI("https://countermeasure.bigrush.com/v1/ingest")
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json",
"Authorization" => "Bearer #{ENV.fetch("COUNTERMEASURE_API_KEY")}")
req.body = {
transactions: [{
order_id: transaction.id.to_s, # unique per store; used to dedupe
occurred_at: transaction.created_at.iso8601,
total: transaction.total.to_f,
email: customer&.email,
phone: customer&.phone,
first_name: customer&.first_name,
last_name: customer&.last_name,
type: transaction.kind, # e.g. "sale" / "buy"
store_id: location.store_number # optional safety check
}]
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
raise "CounterMeasure #{res.code}: #{res.body}" unless res.code == "202"Privacy
Email, phone and names are hashed with SHA-256 before they are stored or sent; raw values are never written to our database or logs. Each store owner attests that customer data was collected in line with Meta’s Business Tools Terms before connecting.