HTTP source
Send signed feedback, events, user traits, and relationships from your server.
- Last reviewed
Use an HTTP source when your server needs to send feedback, user traits, events, or
relationships directly to getuserfeedback.com. It accepts feedback, identify, track,
graph.set, graph.connect, and graph.disconnect.
Set up and send a batch
- Open Integrations, create an HTTP source under Data in, and open its detail page.
- Copy the endpoint URL and Signing secret from that page. A production
URL looks like
https://http.ingest.getuserfeedback.com/int_<id>; use the exact URL shown for your integration. - Send a JSON batch with a signature over the complete, unmodified request
body. For example, from a shell with
curlandopenssl:
url='https://http.ingest.getuserfeedback.com/int_<id>' # Replace with your copied URLsecret='<signing-secret>'body='{"version":1,"batch":[{"type":"identify","messageId":"user-123-v1","userId":"user-123","traits":{"plan":"pro"}}]}'signature=$(printf %s "$body" | openssl dgst -sha256 -hmac "$secret" -binary | od -An -tx1 | tr -d ' \n')curl -i -X POST "$url" \-H 'Content-Type: application/json' \-H "X-GX-Signature: v1=$signature" \--data-binary "$body"Keep the signing secret on your server. The signature is HMAC-SHA256 of the
entire body as sent, formatted as v1= followed by 64 lowercase hex
characters. Changing whitespace or re-serializing the JSON after signing
invalidates it.
When rotating the signing secret, update your server's copy before the old secret expires. Signing-key changes usually reach ingestion within five minutes. During a refresh failure, a revoked key can still be accepted for up to four hours from the last successful configuration check.
Batch format
The top-level JSON object must contain version: 1 and a nonempty batch
array. Each operation needs its own nonblank messageId and may include a
timestamp (an ISO 8601 UTC date-time ending in Z, with seconds or
milliseconds). If omitted, the acceptance time is used.
For non-user graph subjects, first create the matching Group in
Groups settings. Use that Group's key as the subject
collection, such as account.
| Operation | Required fields | Effect |
|---|---|---|
feedback | answers; optional identities, properties, formId, and consent | Collects a Response without requiring a Flow or app. |
identify | At least one of userId or anonymousId; optional traits object | Supplies user identity and traits. Both IDs may be supplied together. |
track | event; optional userId, anonymousId, and properties object | Records an event, with or without user identity. |
graph.set | Non-user subject: { collection, id } and properties object | Supplies properties for that subject. |
graph.connect | from and to subject objects | Connects the directed pair. |
graph.disconnect | from and to subject objects | Disconnects the directed pair. |
For example, {"collection":"account","id":"acct-123"} is a non-user
subject. Use the same from and to pair to disconnect a connection. To send
a user event, include an operation such as:
{"type": "track","messageId": "dashboard-opened-123","userId": "user-123","event": "Dashboard Opened","properties": { "surface": "web" }}Omit both IDs on track to record an event without a user actor. Supplied IDs
must be nonblank strings, at most 255 characters after trimming and
normalization. track and identify do not accept a subject object;
collection-qualified refs remain explicit fields of graph operations.
Send at most 100 operations and 900,000 bytes of JSON per batch. Across a batch, keep to 100 total identify traits, 100 total track property fields (including fields inside nested objects), and 100 total graph properties. Arrays count as one property value; fields inside array items are not separate event-property paths.
Collect feedback
Send feedback through the same integration and signing secret:
{"version": 1,"batch": [{"type": "feedback","messageId": "feedback-123","answers": [{ "value": "Love this app!" }],"identities": [{ "type": "userId", "value": "user-123" }],"properties": { "surface": "checkout" },"consent": ["analytics.measurement"]}]}Each answer has a value. Quantity, scale, and choice answers require a nested
question definition; text answers can omit it. For the shape and examples,
see the shared
feedback answer format. You can omit
identities to collect feedback without a respondent identifier. Supplied
identities use generic {type, value} pairs; your identity settings control
which types participate in matching. Source authentication does not verify
ownership of those identities.
Omitted consent collects the Response without respondent analytics. To permit
analytics, supply "granted" or a scope array containing
"analytics.measurement". "denied", "pending", "revoked", [], and
scope lists without measurement collect feedback without respondent analytics.
These instructions apply to this operation; they do not update a person's consent
state or prove that the person gave consent. Widget consent defaults are unchanged.
Each feedback operation is limited to 64 KiB of serialized JSON, 100 answers, and 24 normalized identities, with identity types and values limited to 255 characters after normalization. Answers and feedback properties do not count toward track-property limits. All content must be valid for JSON storage.
Responses and retries
202 {"accepted":true} means the batch was accepted and is pending processing.
It does not mean downstream processing or audience updates have completed.
Processing can take minutes, and backlogs can make it take longer. A repeated
messageId within the same integration, while its source evidence remains
retained, does not replace stored input: the first stored operation wins,
including when an ID repeats within one batch. Use a new ID for a changed
operation. This applies across operation types, including feedback. Erasure,
retention, or source removal can release that message ID; sending it again then
collects a new operation. The acknowledgement contains no Response ID.
Source configuration changes typically propagate within five minutes. A previously verified configuration can remain usable from cache for up to four hours during a service failure; service failures are retryable.
Fix the request before retrying a 400 (invalid request), 413 (body too
large), or 415 (unsupported media type). 401 {"error":"Unauthorized"}
means the source is unknown, paused, deleted, or the signature is invalid; the
same response is used for each case. A service error is retryable. Retry with
the same message IDs so already stored operations are not replaced.