Skip to main content

API Reference

Complete HTTP API specification for EWP v1.

Base Path

All endpoints are under /ewp/ prefix. Implementations MUST NOT use this prefix for non-protocol endpoints.

Common Conventions

JSON field naming: All JSON field names in request and response bodies use camelCase.

Error format: All error responses use a consistent JSON envelope:

{ "error": "ERROR_CODE" }

Timestamps: Timestamps appear in two forms: timestamp (Unix epoch seconds, integer) for protocol operations; createdAt / updatedAt (ISO 8601 UTC string) for human readability. The timestamp integer is always canonical.

Transport: All EWP communication MUST use HTTPS.

Read Endpoints (No Auth)

GET /ewp/profile

Returns the canonical identity and metadata for this node.

Response 200 OK:

{
"address": "0x1234567890abcdef1234567890abcdef12345678",
"url": "https://node.example.com",
"title": "My Node",
"description": "A personal publishing node",
"ewpVersion": "1",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-03-01T12:00:00.000Z"
}

When description is not set, its value MUST be null (not absent, not "").

StatusCode
500INTERNAL_ERROR

GET /ewp/avatar

Returns the node's avatar as a binary image. This endpoint is OPTIONAL; nodes without an avatar MUST return 404 AVATAR_NOT_SET.

Supported formats: JPEG, PNG, WebP, GIF.

StatusCode
404AVATAR_NOT_SET
500INTERNAL_ERROR

GET /ewp/publications

Returns a paginated index of this node's signed publications.

Query Parameters:

ParameterTypeDefaultDescription
sinceintegerUnix timestamp (seconds). Return only publications with timestamp strictly greater than this value.
limitinteger100Results per page. Range: 1–1000.
pageinteger1Page number (1-indexed).

Response 200 OK:

{
"data": [
{
"contentHash": "0xabc123def456...",
"publisherAddress": "0x1234567890abcdef1234567890abcdef12345678",
"signature": "0x3040...",
"timestamp": 1705312800,
"createdAt": "2026-01-15T10:30:00.000Z",
"contentKind": "POST",
"slug": "my-first-post"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}
}

Notes:

  • Results are ordered by timestamp ascending.
  • Only signed publications are returned.
  • slug is a human-friendly alias and MAY be null.
StatusCode
400INVALID_LIMIT
400INVALID_PAGE
400INVALID_SINCE
500INTERNAL_ERROR

GET /ewp/contents/:contentHash

Returns the Content Unit identified by the given Content Hash.

Path Parameters:

ParameterDescription
contentHashSHA-256 hash with 0x prefix (66 chars total)

Query Parameters:

ParameterTypeDescription
timestampintegerUnix timestamp to scope the lookup
thumbstringThumbnail size for images: sm, md, or lg

Response Headers (Markdown):

Content-Type: text/markdown; charset=utf-8
Cache-Control: public, immutable, max-age=31536000

Response Headers (Binary):

Content-Type: image/png
Content-Disposition: inline; filename="image.png"
Cache-Control: public, immutable, max-age=31536000
Accept-Ranges: bytes
StatusCode
400INVALID_HASH_FORMAT
400INVALID_TIMESTAMP
400INVALID_THUMBNAIL_SIZE
404CONTENT_NOT_FOUND
500INTERNAL_ERROR

Write Endpoints (EIP-712 Auth)

POST /ewp/connections

Accepts a signed follow intent and creates a connection record.

Auth: EIP-712 signature (signer = follower)

Request Body:

{
"typedData": {
"domain": { "name": "epress world", "version": "1", "chainId": 1 },
"types": { ... },
"primaryType": "CreateConnection",
"message": {
"followerAddress": "0xfollower...",
"followeeAddress": "0xfollowee...",
"followeeUrl": "https://followee.example.com",
"followerUrl": "https://follower.example.com",
"timestamp": 1705312800
}
},
"signature": "0x..."
}

Response: 201 Created

{ "status": "created" }
StatusCodeDescription
400INVALID_PAYLOADMissing or malformed fields
400INVALID_SIGNATURESignature verification failed
400INVALID_TIMESTAMPTimestamp outside ±1-hour window
400INVALID_URL_FORMATURL fields are not valid HTTPS URLs
401FOLLOWEE_IDENTITY_MISMATCHProfile at followeeUrl doesn't match followeeAddress
401FOLLOWER_IDENTITY_MISMATCHProfile at followerUrl doesn't match followerAddress
409CONNECTION_ALREADY_EXISTSConnection already exists
500INTERNAL_ERROR

DELETE /ewp/connections

Accepts a signed connection termination request.

Auth: EIP-712 signature (follower for Unfollow; followee for RemoveFollower)

HTTP DELETE with Body

This endpoint uses HTTP DELETE with a JSON request body. EWP traffic is node-to-node HTTPS; implementations MUST NOT route through intermediaries that strip request bodies.

The receiver determines the operation from the recovered signer:

  • Signer = followerAddress → Unfollow
  • Signer = followeeAddress → RemoveFollower

Request Body:

{
"typedData": {
"domain": { "name": "epress world", "version": "1", "chainId": 1 },
"types": { ... },
"primaryType": "DestroyConnection",
"message": {
"followerAddress": "0xfollower...",
"followeeAddress": "0xfollowee...",
"timestamp": 1705312800
}
},
"signature": "0x..."
}

Response: 204 No Content

StatusCodeDescription
400INVALID_PAYLOADMissing or malformed fields
400INVALID_SIGNATURESignature verification failed, or signer matches neither address
400INVALID_TIMESTAMPTimestamp outside ±1-hour window
404CONNECTION_NOT_FOUNDNo matching connection record
409STALE_REQUESTConnection was re-established after this termination was signed
500INTERNAL_ERROR

POST /ewp/publications

Accepts an inbound Proof of Source from a followed publisher.

Auth: Active follow relationship + EIP-712 signature

Request Headers:

HeaderRequiredDescription
X-Epress-Node-UpdatedOPTIONALISO 8601 UTC timestamp of publisher's current updatedAt

Response: 202 Accepted

{ "status": "accepted" }
StatusCodeDescription
400INVALID_PAYLOADMissing or malformed fields
400INVALID_SIGNATURESignature verification failed
401NOT_FOLLOWINGpublisherAddress not in receiver's follow list
409REPLICATION_ALREADY_EXISTSPublication already stored
500INTERNAL_ERROR

PATCH /ewp/nodes/:address

Applies a signed profile update from a remote node.

Auth: EIP-712 signature (signer = node owner)

Path Parameters:

ParameterDescription
addressThe node owner's Ethereum address (EIP-55 checksummed)

Request Body:

{
"typedData": {
"domain": { "name": "epress world", "version": "1", "chainId": 1 },
"types": { ... },
"primaryType": "NodeProfileUpdate",
"message": {
"ownerAddress": "0x1234...",
"url": "https://updated.example.com",
"title": "Updated Title",
"description": "New description",
"timestamp": 1705399200
}
},
"signature": "0x..."
}

Response: 204 No Content

StatusCodeDescription
400INVALID_PAYLOADMissing or malformed fields
400ADDRESS_MISMATCHPath :address does not match ownerAddress
400INVALID_ADDRESSAddress is not a valid EIP-55 checksummed Ethereum address
400INVALID_SIGNATURESignature verification failed
400URL_VERIFICATION_FAILEDNew URL's /ewp/profile did not return matching address
500INTERNAL_ERROR