Home

/

Documentation

/

Webmention API Reference

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

ParameterRequiredDescription
sourceYesURL of the page containing the mention
targetYesURL being mentioned on the source page

Both parameters MUST be valid URLs.

Response Codes

CodeMeaningDescription
200Success (Synchronous)Mention verified and accepted
201Created (Synchronous)Mention created/updated
202Accepted (Asynchronous)Queued for verification
400Bad RequestInvalid parameters
404Not FoundTarget URL not found
410GoneSource 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:

  1. Accept POST requests
  2. Validate source and target parameters
  3. Return appropriate HTTP status code
  4. 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:

TypePropertyDescription
Replyin-reply-toResponse to your post
Likelike-ofSomeone liked your content
Repostrepost-ofSomeone shared your content
Bookmarkbookmark-ofSomeone bookmarked your content
Mention(default)Generic reference
RSVPrsvpEvent 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:

  1. Check if a mention already exists from this source
  2. Re-verify the mention
  3. 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:

  1. Vouch Extension: Require vouches from trusted sources
  2. Manual Moderation: Queue first-time senders for review
  3. Allowlists: Only accept from known domains
  4. 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):

  1. Exchange public keys
  2. Encrypt mention payload
  3. Include encrypted data in custom parameter
  4. 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.

By

Commune Software