Webmention API Reference
Complete API reference for implementing Webmention endpoints and clients.
This document provides a complete API reference for implementing Webmentions according to the W3C Webmention specification.
Endpoint Discovery
Webmention receivers MUST advertise their endpoint using one of these methods:
HTTP Link Header
HTTP/1.1 200 OK
Link: <https://example.com/webmention>; rel="webmention"
Content-Type: text/html
HTML Link Element
<link rel="webmention" href="https://example.com/webmention" />
HTML Anchor Element
<a href="https://example.com/webmention" rel="webmention">webmention endpoint</a>
Note: The Link header takes precedence if multiple methods are present.
Sending Webmentions
Request Format
Send Webmentions via HTTP POST with application/x-www-form-urlencoded encoding:
POST /webmention HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
source=https://sender.example/post&target=https://example.com/article
Request Parameters
| Parameter | Required | Description |
|---|---|---|
source | Yes | URL of the page containing the mention |
target | Yes | URL being mentioned on the source page |
Both parameters MUST be valid URLs.
Response Codes
| Code | Meaning | Description |
|---|---|---|
200 | Success (Synchronous) | Mention verified and accepted |
201 | Created (Synchronous) | Mention created/updated |
202 | Accepted (Asynchronous) | Queued for verification |
400 | Bad Request | Invalid parameters |
404 | Not Found | Target URL not found |
410 | Gone | Source or target no longer exists |
Example Responses
Synchronous Processing (200 OK)
HTTP/1.1 200 OK
Content-Type: text/plain
Webmention accepted and verified
Asynchronous Processing (202 Accepted)
HTTP/1.1 202 Accepted
Content-Type: text/plain
Webmention queued for verification
Error Response (400 Bad Request)
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "invalid_request",
"error_description": "Missing source parameter"
}
Receiving Webmentions
Endpoint Implementation
A minimal Webmention endpoint MUST:
- Accept POST requests
- Validate
sourceandtargetparameters - Return appropriate HTTP status code
- Verify the mention (synchronously or asynchronously)
Validation
Parameter Validation
function validateParameters(source, target) {
// Check both parameters exist
if (!source || !target) {
throw new Error('Missing required parameters');
}
// Validate URLs
try {
new URL(source);
new URL(target);
} catch (e) {
throw new Error('Invalid URL format');
}
// Prevent self-mentions (optional)
if (new URL(source).host === new URL(target).host) {
throw new Error('Source and target cannot be the same domain');
}
return true;
}
Target Ownership
Verify the target URL belongs to your domain:
function isValidTarget(target) {
const targetUrl = new URL(target);
return targetUrl.host === 'yourdomain.com';
}
Verification Process
After accepting a Webmention, verify the mention exists:
1. Fetch Source Page
async function fetchSource(url) {
const response = await fetch(url, {
headers: {
'User-Agent': 'YourWebmentionBot/1.0'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch source: ${response.status}`);
}
return await response.text();
}
2. Verify Target Link
function verifyLinkExists(html, target) {
// Simple check - just look for the URL
if (!html.includes(target)) {
throw new Error('Target URL not found in source');
}
// Better: Parse HTML and check anchor tags
const links = extractLinks(html);
return links.some(link => link === target);
}
3. Parse Microformats (Optional)
Extract semantic data from the source:
import { mf2 } from 'microformats-parser';
function parseSource(html, url) {
const data = mf2(html, { baseUrl: url });
// Find h-entry
const entry = data.items.find(item =>
item.type.includes('h-entry')
);
if (!entry) return null;
return {
type: determineMentionType(entry),
author: extractAuthor(entry),
content: extractContent(entry),
published: entry.properties.published?.[0]
};
}
Mention Types
Determine the type of mention from microformats:
| Type | Property | Description |
|---|---|---|
| Reply | in-reply-to | Response to your post |
| Like | like-of | Someone liked your content |
| Repost | repost-of | Someone shared your content |
| Bookmark | bookmark-of | Someone bookmarked your content |
| Mention | (default) | Generic reference |
| RSVP | rsvp | Event response (yes/no/maybe) |
Detection Logic
function determineMentionType(entry) {
const props = entry.properties;
if (props['in-reply-to']) return 'reply';
if (props['like-of']) return 'like';
if (props['repost-of']) return 'repost';
if (props['bookmark-of']) return 'bookmark';
if (props['rsvp']) return 'rsvp';
return 'mention';
}
Updating and Deleting
Updating Mentions
When a source page changes, send a new Webmention with the same source and target. The receiver should:
- Check if a mention already exists from this source
- Re-verify the mention
- Update or delete based on current state
Deleting Mentions
Source Removed the Link
If verification fails (link no longer exists), mark the mention as deleted or remove it.
Source Page Deleted (410 Gone)
async function checkSourceStatus(url) {
const response = await fetch(url, { method: 'HEAD' });
if (response.status === 410) {
// Source explicitly marked as gone
return 'deleted';
}
if (response.status === 404) {
// Source not found (may be temporary)
return 'not_found';
}
return 'active';
}
Rate Limiting
Implement rate limiting to prevent abuse:
const rateLimit = {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: 'Too many Webmentions, please try again later'
};
Security Considerations
SSRF Protection
Prevent Server-Side Request Forgery:
function isAllowedUrl(url) {
const parsed = new URL(url);
// Block private IP ranges
const privateRanges = [
/^127\./,
/^10\./,
/^172\.(1[6-9]|2[0-9]|3[0-1])\./,
/^192\.168\./,
/^::1$/,
/^fc00:/
];
return !privateRanges.some(range => range.test(parsed.hostname));
}
Content Sanitization
Always sanitize displayed content:
import DOMPurify from 'dompurify';
function sanitizeContent(html) {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'a'],
ALLOWED_ATTR: ['href']
});
}
Spam Prevention
Implement these strategies:
- Vouch Extension: Require vouches from trusted sources
- Manual Moderation: Queue first-time senders for review
- Allowlists: Only accept from known domains
- Content Analysis: Filter obvious spam patterns
Extensions
Vouch
The Vouch extension adds trust verification:
POST /webmention HTTP/1.1
Content-Type: application/x-www-form-urlencoded
source=https://sender.example/post
&target=https://example.com/article
&vouch=https://trusted.example/link-to-sender
Private Webmentions
Use encryption for private mentions (not standardized):
- Exchange public keys
- Encrypt mention payload
- Include encrypted data in custom parameter
- Decrypt server-side
Code Examples
Complete Endpoint (Node.js/Express)
app.post('/webmention', async (req, res) => {
const { source, target } = req.body;
try {
// Validate
if (!source || !target) {
return res.status(400).json({
error: 'Missing required parameters'
});
}
// Verify ownership
if (!isValidTarget(target)) {
return res.status(400).json({
error: 'Target not on this domain'
});
}
// Queue for async processing
await queue.add('verify-webmention', {
source,
target,
receivedAt: new Date()
});
res.status(202).send('Accepted for processing');
} catch (error) {
res.status(400).json({
error: error.message
});
}
});
Discovery Client
async function discoverEndpoint(url) {
const response = await fetch(url);
// Check Link header
const linkHeader = response.headers.get('link');
if (linkHeader) {
const match = linkHeader.match(
/<([^>]+)>;\s*rel="webmention"/
);
if (match) {
return new URL(match[1], url).href;
}
}
// Check HTML
const html = await response.text();
const linkMatch = html.match(
/<link[^>]+rel="webmention"[^>]+href="([^"]+)"/
);
if (linkMatch) {
return new URL(linkMatch[1], url).href;
}
return null;
}
Testing
Use these tools to test your implementation:
- Webmentions.com: Our hosted service for sending, receiving, and testing mentions
- webmention.rocks: Comprehensive test suite for spec compliance
- Telegraph: Alternative manual sending tool
- curl: Command-line testing
# Test sending a Webmention
curl -i -X POST https://example.com/webmention \
-d "source=https://your-site.com/post" \
-d "target=https://example.com/article"
Further Reading
Related Documentation
Getting Started with Webmentions
A comprehensive guide to understanding and implementing Webmentions on your website.