Everything you need to know about Signal shoot
One-way feedback
Recommended for getting started
Accepts feedback from your users only — no individual replies from the operator. Choose this if you do not need two-way conversations. Safe to embed the API key in your mobile app binary. No backend proxy required.
Two-way replies
Required for two-way replies
Enables individual replies to user feedback. Choose this when you need two-way conversations. Requires opaque user IDs, backend proxy for web apps, server-side parent_id validation, and HMAC signing on every user_id-bearing request. See API Reference → Authentication & Keys for the full specification.
Security requirements — read before implementing
Signal shoot works in three steps: (1) Your service sends an HTTP POST request to the Signal shoot server when a user submits feedback. (2) The server stores the feedback in your dashboard. (3) You open the dashboard to read, organize, and respond. No SDK required. Any language or framework that can send an HTTP POST request is supported.
After signing in to Signal shoot, go to the Settings page. Your App ID and Ingest URL are always visible there. Your API keys (live and test) are only displayed once — at your first sign-in or immediately after regeneration. If you do not see your keys, click Regenerate in Settings. You need three values:
Endpoint: https://api.signalshoot.com/v1/ingest/YOUR_APP_ID Header: X-API-Key: YOUR_API_KEY
In all code examples below, replace YOUR_APP_ID with your actual App ID and YOUR_API_KEY with your actual key. Use the test key during development and the live key in production. App ID is always visible in Settings. If your API keys are not visible, click Regenerate to create new ones — Signal shoot displays them only once.
When your app sends feedback to Signal shoot, it sends a JSON object. Here is what each field does:
For detailed specs (character limits, validation, usage examples), see the API Reference tab.
Choose your platform below and copy the code. The integration is a single HTTP POST request — no library or SDK to install. Paste the code into your app wherever you want users to be able to send feedback (a contact form, a bug report button, a feedback modal, etc.).
One-way feedback:sample code for receive-only integration
React Native / Expo
const sendFeedback = async (type, message, channel = 'contact') => {
await fetch('https://api.signalshoot.com/v1/ingest/YOUR_APP_ID', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'YOUR_API_KEY',
},
body: JSON.stringify({
type, message, channel,
metadata: { app_version: Constants.expoConfig?.version, os: Platform.OS },
}),
});
};Flutter (Dart)
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> sendFeedback(String type, String message) async {
await http.post(
Uri.parse('https://api.signalshoot.com/v1/ingest/YOUR_APP_ID'),
headers: {'Content-Type': 'application/json', 'X-API-Key': 'YOUR_API_KEY'},
body: jsonEncode({'type': type, 'message': message, 'channel': 'contact'}),
);
}Swift (iOS)
func sendFeedback(type: String, message: String) async throws {
var req = URLRequest(url: URL(string: "https://api.signalshoot.com/v1/ingest/YOUR_APP_ID")!)
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.setValue("YOUR_API_KEY", forHTTPHeaderField: "X-API-Key")
req.httpBody = try JSONSerialization.data(withJSONObject: [
"type": type, "message": message, "channel": "contact"
])
let (_, _) = try await URLSession.shared.data(for: req)
}Kotlin (Android)
val body = JSONObject().apply {
put("type", "bug"); put("message", msg); put("channel", "contact")
}.toString().toRequestBody("application/json".toMediaType())
val request = Request.Builder()
.url("https://api.signalshoot.com/v1/ingest/YOUR_APP_ID")
.addHeader("X-API-Key", "YOUR_API_KEY")
.post(body).build()
OkHttpClient().newCall(request).execute()Web (JavaScript)
await fetch('https://api.signalshoot.com/v1/ingest/YOUR_APP_ID', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': 'YOUR_API_KEY' },
body: JSON.stringify({ type: 'bug', message: msg, channel: 'contact' }),
});Important for web apps: live and test keys (fb_live_*, fb_test_*) must never appear in client-side JavaScript — anyone can open DevTools and copy them. For browser-only ingest, use a publishable key (fb_pub_*) issued from Settings, which is safe to ship to the browser because it is write-only, gated by an Origin allowlist, and rate-limited per source IP. If your integration needs user_id or parent_id (replies, threaded conversations, user-data deletion), publishable keys cannot be used; route those calls through your own backend with a live key.
Including user_id when sending feedback enables two-way communication. parent_id is the conversation ID you reply to. Reuse the id from the initial submission when the user sends a follow-up about the same issue. Flow: (1) User sends feedback with user_id. → The API returns a feedback ID (e.g. { id: "fb_xyz789" }). Save this ID in your app. (2) You write a reply from the dashboard. (3) Your app calls the replies API and displays the reply to the user. (4) If the user wants to reply back, send a new request with parent_id set to the ID saved in (1) (e.g. fb_xyz789). Notes: ・Only use IDs returned by the API. Do not accept arbitrary client input for parent_id. ・Follow-up messages with parent_id do not count toward the monthly limit.
Example: sending feedback with user_id, then fetching replies. See API Reference → Authentication & Keys for HMAC details.
// Include user_id when sending (X-Signal-User-Id-Signature required):
{ type: 'bug', message: '...', user_id: 'a1b2c3d4-e5f6-4789-a0b1-c2d3e4f56789' }
// Fetch replies:
GET https://api.signalshoot.com/v1/feedback/YOUR_APP_ID/replies?user_id=a1b2c3d4-e5f6-4789-a0b1-c2d3e4f56789
X-API-Key: fb_live_YOUR_KEY
// Sign the user_id and send the hex digest in this header (required):
X-Signal-User-Id-Signature: <64-char hex HMAC-SHA256(signing_secret, user_id)>Once your integration is ready, try it out. Send a real message from your app or service and confirm it arrives. Using a test key (fb_test_...) marks the feedback as test data, which you can show or hide in the inbox using the filter toggle. Steps: (1) Send a request with your test API key (fb_test_...) from cURL, Postman, or your app's test environment. (2) Open the dashboard inbox and enable the "Test data" toggle to confirm arrival. (3) Try changing the status, adding tags, and writing a reply. (4) If you implemented replies, verify your app can fetch them. (5) Once reception and replies work correctly, switch from the test API key to the live API key.
curl -X POST https://api.signalshoot.com/v1/ingest/YOUR_APP_ID \
-H "Content-Type: application/json" \
-H "X-API-Key: fb_test_YOUR_KEY" \
-d '{"type":"bug","channel":"contact","message":"Test from cURL"}'Using a test key lets you verify under production-equivalent conditions. Test keys expire 1 hour after generation. Test submissions do not count toward your monthly feedback limit.
You can also let an AI coding assistant handle the setup. See the "AI Setup" tab for details.
AI Setup →