API
Use the developer API and Webhooks for custom integrations.
API
Mobyform provides a RESTful API for developers to programmatically manage forms, read data, and build custom integrations.
Requires Pro plan or above
Authentication
API Keys
- Go to "Settings" → "Developer"
- Click "Generate API Key"
- Copy and securely store the key
Usage
Include the API key in request headers:
Authorization: Bearer YOUR_API_KEYKey Management
- Create multiple API keys
- Set permission scopes per key
- Revoke keys at any time
- View key usage
Main APIs
Form Management
| Endpoint | Method | Description |
|---|---|---|
/api/forms | GET | List forms |
/api/forms/:key | GET | Get form details |
/api/forms | POST | Create a form |
/api/forms/:key | PUT | Update a form |
/api/forms/:key | DELETE | Delete a form |
Form Data
| Endpoint | Method | Description |
|---|---|---|
/api/forms/:key/data | GET | List submission data |
/api/forms/:key/data/:id | GET | Get single submission |
/api/forms/:key/data | POST | Add submission data |
/api/forms/:key/data/:id | PUT | Update submission data |
/api/forms/:key/data/:id | DELETE | Delete submission data |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
page | number | Page number |
size | number | Items per page |
sort | string | Sort field |
order | string | Sort direction (asc/desc) |
filter | object | Filter conditions |
Webhooks
Configure Webhooks
Set up webhooks via API or the management panel:
{
"url": "https://your-server.com/webhook",
"events": ["form_data_add", "form_data_update", "form_data_delete"],
"headers": {
"X-Custom-Header": "value"
}
}Webhook Events
| Event | Trigger |
|---|---|
form_data_add | New data submitted |
form_data_update | Data modified |
form_data_delete | Data deleted |
Webhook Payload
{
"event": "form_data_add",
"formKey": "abc123",
"data": {
"id": "data_001",
"fields": {
"name": "John Doe",
"email": "john@example.com"
},
"createdAt": "2024-01-15T10:30:00Z"
}
}Verifying Webhooks
Verify webhook request authenticity to ensure it comes from Mobyform:
- Check the request source IP
- Validate signature headers (if configured)
Rate Limits
API requests are protected by rate limits:
| Plan | Limit |
|---|---|
| Pro | 1,000 requests/month |
| Business | 10,000 requests/month |
| Enterprise | Custom |
Exceeding the limit returns 429 Too Many Requests.
Error Handling
HTTP Status Codes
| Code | Description |
|---|---|
200 | Success |
201 | Created |
400 | Bad request parameters |
401 | Unauthorized (invalid API key) |
403 | Forbidden (insufficient permissions) |
404 | Resource not found |
429 | Too many requests |
500 | Internal server error |
Error Response Format
{
"error": {
"code": "INVALID_REQUEST",
"message": "Description of the error"
}
}Code Examples
cURL — List Forms
# List all forms with pagination
curl -X GET "https://api.mobyform.com/api/forms?page=1&size=10" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"JavaScript/Node.js — Get Form Data
// Fetch submission data for a specific form
const response = await fetch(
'https://api.mobyform.com/api/forms/abc123/data?page=1&size=20',
{
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
}
);
const result = await response.json();
console.log(`Total submissions: ${result.total}`);
console.log('Data:', result.data);Python — Create a Form Submission
import requests
# Submit data to a form
url = "https://api.mobyform.com/api/forms/abc123/data"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
}
payload = {
"fields": {
"name": "Jane Smith",
"email": "jane@example.com",
"feedback": "Great product!"
}
}
response = requests.post(url, json=payload, headers=headers)
print(f"Status: {response.status_code}")
print(f"Created: {response.json()}")Pagination
All list endpoints support pagination through page and size query parameters.
page— The page number to retrieve (starts from 1)size— Number of items per page (default: 20, max: 100)
Example Request:
GET /api/forms?page=2&size=10Example Response:
{
"data": [...],
"total": 56,
"page": 2,
"size": 10,
"totalPages": 6
}If no pagination parameters are provided, the API defaults to page=1 and size=20.
Error Codes
The API uses structured error codes in the response body to help you handle errors programmatically:
| Code | Name | Description |
|---|---|---|
INVALID_REQUEST | Invalid Request | The request body or parameters are malformed |
AUTHENTICATION_FAILED | Authentication Failed | The API key is missing, invalid, or expired |
PERMISSION_DENIED | Permission Denied | The API key does not have the required scope |
RESOURCE_NOT_FOUND | Resource Not Found | The requested form, submission, or resource does not exist |
RATE_LIMITED | Rate Limited | You have exceeded the allowed number of requests |
VALIDATION_ERROR | Validation Error | The submitted data failed field validation rules |
INTERNAL_ERROR | Internal Error | An unexpected server error occurred; try again later |
Webhook Signature Verification
When you configure a webhook secret, Mobyform signs every webhook payload using HMAC-SHA256. The signature is sent in the X-Webhook-Signature header. You should verify this signature to confirm the request is authentic.
Node.js Verification
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)
);
}
// Usage in an Express handler
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const rawBody = JSON.stringify(req.body);
if (!verifyWebhookSignature(rawBody, signature, 'YOUR_WEBHOOK_SECRET')) {
return res.status(401).send('Invalid signature');
}
// Process the webhook event
console.log('Verified event:', req.body.event);
res.status(200).send('OK');
});Python Verification
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# Usage in a Flask handler
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Webhook-Signature', '')
if not verify_webhook_signature(request.data, signature, 'YOUR_WEBHOOK_SECRET'):
return 'Invalid signature', 401
event = request.json
print(f"Verified event: {event['event']}")
return 'OK', 200Best Practices
- Secure your keys — Never expose API keys in frontend code
- Handle errors — Implement proper error handling and retry logic
- Respect rate limits — Control request frequency appropriately
- Use Webhooks — Prefer webhooks for real-time notifications over polling
Next Steps
- Custom Domain — Configure your own domain
- Integrations — Use built-in integrations without coding