Product Guide

Guide

Everything you need to know about Signal shoot

Setup Guide

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

!user_id must be an opaque identifier (UUID, ULID, or random token). Must not be a sequential database ID. A leaked API key combined with sequential IDs allows enumeration of user feedback.
!Never embed a live or test key (fb_live_*, fb_test_*) in client-side JavaScript — anyone can copy it from DevTools. For browser-only ingest, generate a publishable key (fb_pub_*) from Settings: it is write-only, gated by an Origin allowlist, and per-IP rate-limited. If you need user_id or parent_id (i.e. replies, threaded conversations, or user-data deletion), publishable keys cannot be used — route those calls through your own backend with a live key.
!parent_id must only be set to feedback IDs that your backend has verified. Do not let the client specify arbitrary IDs.
!Signal shoot displays API keys once on generation. Store them securely. Do not commit them to public repositories.

How It Works (Overview)

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.

Step 1: Get Your Credentials

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:

App ID — A unique identifier for your app (e.g. fb_a1b2c3d4e5). Include this in the URL when sending feedback.
Live API Key — Starts with fb_live_. Use this in your production app. Feedback sent with this key appears in your main inbox.
Test API Key — Starts with fb_test_. Use this during development. Expires 1 hour after generation. Test feedback is stored separately and does not clutter your production inbox.
Your credentials
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.

Step 2: Understand the Request Format

When your app sends feedback to Signal shoot, it sends a JSON object. Here is what each field does:

type (required): Classification label. Displayed as-is in the dashboard (any language works).
message (required): Feedback body text. Max 5,000 characters.
channel (optional): Identifies the screen or entry point. Defaults to "default".
user_id (required for two-way replies): Opaque user identifier (UUID/ULID), 1–255 chars.
parent_id (used in two-way replies): Feedback ID from a previous response, 1–50 chars. See Step 4.
metadata (optional): JSON object with device info, app version, etc. Max 8 KB serialized. See API Reference for full validation rules.

For detailed specs (character limits, validation, usage examples), see the API Reference tab.

Step 3: Add the Code to Your App

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

Production-ready — React Native
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)

Production-ready — Flutter
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)

Production-ready — Swift
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)

Production-ready — Kotlin
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)

Example only — do not use in production (Web client-side)
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.

Step 4: Enable Two-Way Communication (Optional)

Two-way replies security requirements apply. user_id must be opaque, Web must use backend proxy, parent_id must be server-verified.

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.

Example — Replies
// 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)>

Step 5: Verify the API Connection Before Going Live

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.

Example — cURL
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.

Alternative: Let AI Write the Code for You

You can also let an AI coding assistant handle the setup. See the "AI Setup" tab for details.

AI Setup