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

  1. Open Integrations, create an HTTP source under Data in, and open its detail page.
  2. 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.
  3. Send a JSON batch with a signature over the complete, unmodified request body. For example, from a shell with curl and openssl:
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.

OperationRequired fieldsEffect
feedbackanswers; optional identities, properties, formId, and consentCollects a Response without requiring a Flow or app.
identifyAt least one of userId or anonymousId; optional traits objectSupplies user identity and traits. Both IDs may be supplied together.
trackevent; optional userId, anonymousId, and properties objectRecords an event, with or without user identity.
graph.setNon-user subject: { collection, id } and properties objectSupplies properties for that subject.
graph.connectfrom and to subject objectsConnects the directed pair.
graph.disconnectfrom and to subject objectsDisconnects 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.