CoolFace
Apppublic

ampSalerno/amperity-ampy-offline-events

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
App README

Ampy Offline Events API — mock

A mock realtime event-streaming destination for a fictional retailer, "Ampy." It exists so an Amperity realtime/streaming connector has a faithful target to stream events at — one event at a time — without standing up any real third-party system.

This is not a real vendor. There are no external docs; the spec is this README plus app.py.

What it models

A retailer's conversions/events API that ingests customer behavior as it happens. The connector under test streams events the moment they occur (or in small micro-batches).

Supported event types (exactly these seven)

EventCanonical tokenExtra required fields
Add to Cartadd_to_cartproperties.product_id
Purchasepurchaseproperties.value, properties.currency
Abandon Cartabandon_cartproperties.product_id
Refund Purchaserefund_purchaseproperties.value, properties.currency
Registerregister—
Logonlogon—
Logofflogoff—

Display-name forms ("Add to Cart", "Refund Purchase") are accepted and normalized to the token. Any other event_name is rejected with AMPY_UNKNOWN_EVENT_NAME.

Auth — the simplest thing that still validates

A single static API key on the Authorization header. No OAuth, no JWT, no token exchange.

  1. 1.Get a key (self-service, no signup):
   GET /_mock/credentials?label=eric-dev

Returns a deterministic api_key bound to tenant eric-dev. Same label → same key, forever (survives Space restarts). Labels use hyphens, not underscores.

  1. 1.Send events with it:
   Authorization: Bearer <api_key>

Wrong or missing key → 401. The label is embedded in the key, so the mock validates a presented key on its own — there is no server-side key store.

Public by design: anyone who knows the (default) salt can derive any label's key. Mocks have no real security.

Endpoints

MethodPathPurpose
POST/v1/eventsStream one event (JSON object) or many (JSON array)
GET/_mock/credentials?label=<label>Self-service API key
GET/_debug/eventsEvents accepted for the caller's tenant
POST/_debug/resetClear the caller's tenant state
GET/_debug/healthLiveness probe

Realtime semantics

  • —Single or batch. Body may be one event object (the realtime case) or an array. A single event returns a flat {status, event_id, received_at}; a batch returns {received, accepted, duplicates, rejected, results[]}.
  • —Freshness. event_time (epoch seconds or ms) must be no more than 7 days old and no more than 5 minutes in the future, else AMPY_STALE_EVENT_TIME.
  • —Idempotency. Supply event_id to dedupe. Re-sending a seen event_id returns status: duplicate and is not double-counted. (Realtime pipelines retry — dedup matters.)
  • —Identity. Every event needs at least one match key:
  • —PII, hashed: email_sha256 and/or phone_sha256 — SHA-256 hashed for privacy. When present, must be a lowercase 64-char hex digest; a raw email/phone is rejected with AMPY_INVALID_MATCH_KEY_HASH. Normalize before hashing (email: trim + lowercase; phone: E.164).
  • —Non-PII, optional: customer_id — the retailer's own customer key. Accepted as-is (not hashed, not shape-validated). Can identify on its own.
  • —At least one of the three is required, else AMPY_MISSING_MATCH_KEY.

Error codes

CodeHTTPMeaning
AMPY_MISSING_EVENT_NAME422event_name absent/blank
AMPY_UNKNOWN_EVENT_NAME422Not one of the seven supported events
AMPY_MISSING_EVENT_TIME422event_time absent
AMPY_INVALID_EVENT_TIME422event_time not a number
AMPY_STALE_EVENT_TIME422Too old or too far in the future
AMPY_MISSING_MATCH_KEY422No email_sha256/phone_sha256/customer_id supplied
AMPY_INVALID_MATCH_KEY_HASH422Identity value isn't a lowercase SHA-256 hex digest (e.g. raw email/phone)
AMPY_MISSING_PURCHASE_VALUE422Monetary event without value
AMPY_MISSING_CURRENCY422Monetary event without 3-letter currency
AMPY_MISSING_PRODUCT422Cart event without product_id
AMPY_INVALID_PAYLOAD / AMPY_INVALID_PROPERTIES422Wrong shape

(For a single-event request, a rejection returns HTTP 422. Inside a batch, each event carries its own status/error_code and the call returns 200.)

Force transport errors for retry testing: X-Mock-Inject-Error: 429|500|503.

Pointing a connector at the mock

SettingValue
Base URL (local)http://127.0.0.1:7860
Base URL (HF)https://ampSalerno-amperity-ampy-offline-events.hf.space
Events endpoint<base>/v1/events
Auth headerAuthorization: Bearer <api_key>

Get a key: ./mint-creds.sh <label> [base-url].

Run it

Local:

bash
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
uvicorn app:app --port 7860

Validate (Newman):

bash
cd postman && ./run.sh local      # or ./run.sh hf after deploy

Deploy to Hugging Face Spaces (free):

bash
./hf-deploy.sh                     # see the script header for prerequisites

Example

bash
KEY=$(curl -s "http://127.0.0.1:7860/_mock/credentials?label=eric-dev" | python3 -c "import sys,json;print(json.load(sys.stdin)['api_key'])")

# email hashed for privacy: echo -n "alex@example.com" | shasum -a 256
EMAIL_HASH=$(printf '%s' "alex@example.com" | shasum -a 256 | cut -d' ' -f1)

curl -X POST http://127.0.0.1:7860/v1/events \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"event_name":"purchase","event_time":'"$(date +%s)"',
       "event_id":"o-55501",
       "match_keys":{"email_sha256":"'"$EMAIL_HASH"'"},
       "properties":{"value":129.99,"currency":"USD","order_id":"o-55501"}}'