Build custom integrations and automate workflows with FeatureShark's comprehensive REST API. This guide covers authentication, endpoints, and best practices for developers.
All API requests should be made to:
https://api.featureshark.com/v1
FeatureShark uses API keys for authentication. Include your API key in the request headers:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
All responses are in JSON format:
{
"success": true,
"data": { /* response data */ },
"meta": {
"page": 1,
"per_page": 25,
"total": 150,
"total_pages": 6
}
}
{
"read": ["requests", "votes", "comments", "users", "analytics"],
"write": ["requests", "votes", "comments", "status_updates"],
"admin": ["settings", "team", "integrations", "billing"]
}
GET /requests
Parameters:
page
(integer): Page number (default: 1)per_page
(integer): Items per page (default: 25, max: 100)category
(string): Filter by categorystatus
(string): Filter by statussort
(string): Sort order (votes
, created_at
, updated_at
)order
(string): Sort direction (asc
, desc
)Example Request:
curl -X GET "https://api.featureshark.com/v1/requests?category=mobile&status=planned&sort=votes&order=desc" \
-H "Authorization: Bearer YOUR_API_KEY"
Example Response:
{
"success": true,
"data": [
{
"id": "req_123abc",
"title": "Dark Mode for Mobile App",
"description": "Add dark theme option to reduce eye strain",
"category": "mobile",
"status": "planned",
"priority": "high",
"votes": 127,
"comments_count": 23,
"created_at": "2024-03-15T10:30:00Z",
"updated_at": "2024-03-20T14:22:00Z",
"user": {
"id": "user_456def",
"name": "Sarah Johnson",
"email": "sarah@example.com",
"avatar_url": "https://..."
},
"tags": ["ui", "mobile", "accessibility"]
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 150,
"total_pages": 6
}
}
GET /requests/{id}
Example Response:
{
"success": true,
"data": {
"id": "req_123abc",
"title": "Dark Mode for Mobile App",
"description": "Add dark theme option to reduce eye strain during night usage...",
"category": "mobile",
"status": "planned",
"priority": "high",
"votes": 127,
"comments_count": 23,
"created_at": "2024-03-15T10:30:00Z",
"updated_at": "2024-03-20T14:22:00Z",
"user": {
"id": "user_456def",
"name": "Sarah Johnson",
"email": "sarah@example.com",
"customer_tier": "enterprise"
},
"assignee": {
"id": "team_789ghi",
"name": "Mike Chen",
"role": "developer"
},
"tags": ["ui", "mobile", "accessibility"],
"custom_fields": {
"effort_estimate": "6 weeks",
"business_value": "high",
"technical_complexity": "medium"
}
}
}
POST /requests
Request Body:
{
"title": "Bulk User Import",
"description": "Allow importing multiple users via CSV file upload",
"category": "admin",
"priority": "medium",
"tags": ["import", "admin", "csv"],
"custom_fields": {
"business_value": "high",
"effort_estimate": "4 weeks"
}
}
Response:
{
"success": true,
"data": {
"id": "req_789xyz",
"title": "Bulk User Import",
"status": "under_review",
"created_at": "2024-03-22T09:15:00Z",
"url": "https://yourapp.featureshark.com/requests/req_789xyz"
}
}
PUT /requests/{id}
Request Body:
{
"status": "in_development",
"assignee_id": "team_789ghi",
"priority": "high",
"custom_fields": {
"sprint": "Sprint 24",
"github_issue": "https://github.com/company/repo/issues/456"
}
}
GET /requests/{id}/votes
Example Response:
{
"success": true,
"data": [
{
"id": "vote_abc123",
"user": {
"id": "user_456def",
"name": "Sarah Johnson",
"customer_tier": "enterprise"
},
"created_at": "2024-03-20T11:30:00Z"
}
],
"meta": {
"total_votes": 127,
"vote_breakdown": {
"enterprise": 45,
"pro": 32,
"free": 50
}
}
}
POST /requests/{id}/votes
Request Body:
{
"user_id": "user_456def"
}
DELETE /requests/{id}/votes/{vote_id}
GET /requests/{id}/comments
Example Response:
{
"success": true,
"data": [
{
"id": "comment_123",
"content": "This would be incredibly helpful for our onboarding process!",
"user": {
"id": "user_456def",
"name": "Sarah Johnson",
"avatar_url": "https://..."
},
"created_at": "2024-03-20T15:45:00Z",
"updated_at": "2024-03-20T15:45:00Z",
"is_internal": false
}
]
}
POST /requests/{id}/comments
Request Body:
{
"content": "We're starting development on this feature next sprint!",
"is_internal": false,
"notify_subscribers": true
}
GET /users
Parameters:
page
, per_page
(pagination)customer_tier
(string): Filter by tieractive
(boolean): Filter by active statussort
(string): Sort by (created_at
, last_active
, requests_count
)GET /users/{id}
Example Response:
{
"success": true,
"data": {
"id": "user_456def",
"name": "Sarah Johnson",
"email": "sarah@example.com",
"customer_tier": "enterprise",
"created_at": "2024-01-15T08:00:00Z",
"last_active": "2024-03-22T16:30:00Z",
"stats": {
"requests_submitted": 8,
"votes_cast": 45,
"comments_posted": 23
}
}
}
GET /analytics/overview
Parameters:
start_date
(date): Start of date rangeend_date
(date): End of date rangegranularity
(string): day
, week
, month
Example Response:
{
"success": true,
"data": {
"summary": {
"total_requests": 247,
"total_votes": 1856,
"total_users": 423,
"active_requests": 189
},
"trends": {
"requests_growth": "+12%",
"user_growth": "+8%",
"engagement_growth": "+15%"
},
"top_categories": [
{"name": "mobile", "count": 67},
{"name": "web", "count": 45},
{"name": "api", "count": 34}
]
}
}
GET /analytics/requests
Example Response:
{
"success": true,
"data": {
"submission_trends": [
{"date": "2024-03-01", "count": 12},
{"date": "2024-03-02", "count": 8},
{"date": "2024-03-03", "count": 15}
],
"category_distribution": {
"mobile": 67,
"web": 45,
"api": 34,
"integrations": 28
},
"status_breakdown": {
"under_review": 23,
"planned": 45,
"in_development": 12,
"testing": 6,
"released": 89,
"declined": 18
}
}
}
GET /analytics/votes
Example Response:
{
"success": true,
"data": {
"vote_trends": [
{"date": "2024-03-01", "votes": 45},
{"date": "2024-03-02", "votes": 38},
{"date": "2024-03-03", "votes": 52}
],
"top_voted_requests": [
{
"id": "req_123abc",
"title": "Dark Mode for Mobile App",
"votes": 127
}
],
"voter_segments": {
"enterprise": 245,
"pro": 156,
"free": 89
}
}
}
POST /webhooks
Request Body:
{
"url": "https://your-app.com/webhooks/featureshark",
"events": ["request.created", "request.status_changed", "vote.added"],
"secret": "your_webhook_secret",
"active": true
}
request.created
- New feature request submittedrequest.updated
- Feature request modifiedrequest.status_changed
- Status transitionrequest.deleted
- Feature request removedvote.added
- New vote castvote.removed
- Vote withdrawncomment.created
- New comment postedcomment.updated
- Comment modifieduser.registered
- New user signed up{
"event": "request.created",
"timestamp": "2024-03-22T10:30:00Z",
"data": {
"request": {
"id": "req_123abc",
"title": "Advanced Search Filters",
"description": "Need ability to filter by multiple criteria",
"category": "search",
"status": "under_review",
"user": {
"id": "user_456def",
"name": "Sarah Johnson",
"customer_tier": "enterprise"
}
}
}
}
{
"event": "request.status_changed",
"timestamp": "2024-03-22T14:15:00Z",
"data": {
"request": {
"id": "req_123abc",
"title": "Advanced Search Filters",
"previous_status": "planned",
"new_status": "in_development",
"assignee": {
"id": "team_789ghi",
"name": "Mike Chen"
}
}
}
}
Verify webhook authenticity using HMAC-SHA256:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
Example Usage:
app.post('/webhooks/featureshark', (req, res) => {
const signature = req.headers['x-featureshark-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Unauthorized');
}
// Process webhook...
res.status(200).send('OK');
});
npm install featureshark-js
Basic Usage:
const FeatureShark = require('featureshark-js');
const client = new FeatureShark({
apiKey: process.env.FEATURESHARK_API_KEY,
baseURL: 'https://api.featureshark.com/v1'
});
// Get all requests
const requests = await client.requests.list({
status: 'planned',
sort: 'votes',
order: 'desc'
});
// Create new request
const newRequest = await client.requests.create({
title: 'Mobile Push Notifications',
description: 'Send push notifications for important updates',
category: 'mobile'
});
// Add vote
await client.votes.add('req_123abc', 'user_456def');
pip install featureshark-python
Basic Usage:
from featureshark import FeatureSharkClient
client = FeatureSharkClient(
api_key=os.environ['FEATURESHARK_API_KEY']
)
# Get requests with filtering
requests = client.requests.list(
category='mobile',
status='planned',
sort='votes'
)
# Create new request
new_request = client.requests.create({
'title': 'Offline Mode',
'description': 'Work without internet connection',
'category': 'mobile',
'priority': 'high'
})
# Update request status
client.requests.update('req_123abc', {
'status': 'in_development',
'assignee_id': 'team_456def'
})
composer require featureshark/php-sdk
Basic Usage:
use FeatureShark\Client;
$client = new Client([
'api_key' => $_ENV['FEATURESHARK_API_KEY']
]);
// List requests
$requests = $client->requests()->list([
'category' => 'api',
'status' => 'planned'
]);
// Create request
$request = $client->requests()->create([
'title' => 'GraphQL API Support',
'description' => 'Add GraphQL endpoint for better data fetching',
'category' => 'api'
]);
# Gemfile
gem 'featureshark-ruby'
# Usage
client = FeatureShark::Client.new(
api_key: ENV['FEATURESHARK_API_KEY']
)
requests = client.requests.list(
category: 'web',
sort: 'votes'
)
import "github.com/featureshark/go-sdk"
client := featureshark.NewClient(os.Getenv("FEATURESHARK_API_KEY"))
requests, err := client.Requests.List(&featureshark.RequestListOptions{
Category: "mobile",
Status: "planned",
})
200 OK
- Success201 Created
- Resource created successfully400 Bad Request
- Invalid request parameters401 Unauthorized
- Invalid or missing API key403 Forbidden
- Insufficient permissions404 Not Found
- Resource not found422 Unprocessable Entity
- Validation errors429 Too Many Requests
- Rate limit exceeded500 Internal Server Error
- Server error{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "title",
"message": "Title is required and must be at least 10 characters"
},
{
"field": "category",
"message": "Category must be one of: mobile, web, api, integration"
}
]
}
}
INVALID_API_KEY
- API key is invalid or expiredINSUFFICIENT_PERMISSIONS
- API key lacks required permissionsRATE_LIMIT_EXCEEDED
- Too many requests in time periodVALIDATION_ERROR
- Request data validation failedRESOURCE_NOT_FOUND
- Requested resource doesn't existDUPLICATE_RESOURCE
- Attempting to create duplicate resourceAlways use pagination for large datasets:
// Good: Use pagination
const getAllRequests = async () => {
let allRequests = [];
let page = 1;
while (true) {
const response = await client.requests.list({
page: page,
per_page: 100
});
allRequests.push(...response.data);
if (page >= response.meta.total_pages) break;
page++;
}
return allRequests;
};
Use API-side filtering instead of client-side:
// Good: Filter on server
const highPriorityRequests = await client.requests.list({
priority: 'high',
status: 'planned',
sort: 'votes',
order: 'desc'
});
// Avoid: Fetching all and filtering client-side
const allRequests = await client.requests.list();
const filtered = allRequests.data.filter(r => r.priority === 'high');
// Good: Use environment variables
const client = new FeatureShark({
apiKey: process.env.FEATURESHARK_API_KEY
});
// Avoid: Hardcoding keys
const client = new FeatureShark({
apiKey: 'fs_1234567890abcdef' // Never do this!
});
Always validate input before sending to API:
const createRequest = (requestData) => {
// Validate required fields
if (!requestData.title || requestData.title.length < 10) {
throw new Error('Title must be at least 10 characters');
}
if (!['mobile', 'web', 'api'].includes(requestData.category)) {
throw new Error('Invalid category');
}
return client.requests.create(requestData);
};
const makeRequestWithRetry = async (fn, maxRetries = 3) => {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
};
const { App } = require('@slack/bolt');
const FeatureShark = require('featureshark-js');
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
const featureShark = new FeatureShark({
apiKey: process.env.FEATURESHARK_API_KEY
});
// Command to create feature request
app.command('/feature-request', async ({ command, ack, respond }) => {
await ack();
const [title, description] = command.text.split(' | ');
try {
const request = await featureShark.requests.create({
title: title.trim(),
description: description?.trim() || 'Created via Slack',
category: 'general'
});
await respond(`✅ Feature request created: ${request.url}`);
} catch (error) {
await respond(`❌ Error: ${error.message}`);
}
});
// Command to check top requests
app.command('/top-requests', async ({ ack, respond }) => {
await ack();
const requests = await featureShark.requests.list({
sort: 'votes',
order: 'desc',
per_page: 5
});
const list = requests.data.map((req, i) =>
`${i + 1}. ${req.title} (${req.votes} votes)`
).join('\n');
await respond(`🔥 Top Feature Requests:\n${list}`);
});
name: Sync Feature Requests
on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 9 AM
jobs:
sync-requests:
runs-on: ubuntu-latest
steps:
- name: Get Planned Features
id: requests
run: |
curl -s -H "Authorization: Bearer ${{ secrets.FEATURESHARK_API_KEY }}" \
"https://api.featureshark.com/v1/requests?status=planned" \
| jq '.data[] | {id, title, votes}' > planned_features.json
- name: Create GitHub Issues
run: |
while IFS= read -r feature; do
title=$(echo "$feature" | jq -r '.title')
votes=$(echo "$feature" | jq -r '.votes')
gh issue create \
--title "Feature: $title" \
--body "User votes: $votes\nFeatureShark ID: $(echo "$feature" | jq -r '.id')" \
--label "feature-request"
done < planned_features.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
// Express.js dashboard endpoint
app.get('/dashboard/data', async (req, res) => {
try {
const [overview, requests, topUsers] = await Promise.all([
featureShark.analytics.overview(),
featureShark.requests.list({
status: 'in_development',
sort: 'votes',
order: 'desc'
}),
featureShark.users.list({
sort: 'requests_count',
order: 'desc',
per_page: 10
})
]);
res.json({
metrics: overview.data.summary,
activeFeatures: requests.data,
topContributors: topUsers.data
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Complete your integration knowledge:
API Version: v1
Rate Limits: Apply to all plans
Last Updated: September 2025
Still need help? Contact our support team