Data Views API
Added in version 2.7.0
The Data Views API enables fine-grained access control over JSON resources in ESS through GraphQL-based filtering. Data subjects can create filtered "View Resources" that expose only specific fields from their source data, allowing selective sharing without exposing sensitive information.
Overview
Up to ESS 2.6, access control was available only at the resource level using Access Control Policies (ACP) or Access Grants. If a resource owner wanted to share specific fields from a resource while keeping other fields private, they had to manually create separate resources for each sharing scenario. This approach makes data harder to manage and can lead to synchronization issues.
The Data Views API solves this problem by introducing View Resources - dynamic resources that present filtered views of source data. View Resources behave like regular Solid resources but their content is automatically derived from other resources (Source Resources) using GraphQL queries. When source data changes, View Resources update automatically.
Key Benefits
Fine-grained sharing: Share specific fields from resources without creating duplicates
Automatic synchronization: View Resources update when source data changes
Reusable filters: View Definitions can be applied to multiple resources
Standard Solid integration: View Resources work with existing ACP, Access Grants, and Notifications
Audit support: View operations generate audit events like regular resources
Use Cases
Personal Data Sharing: Share contact information (name and email) while keeping phone numbers and addresses private
Business Data: Share transaction amounts and categories while hiding account numbers and merchant details
Collaborative Workflows: Share project status information while keeping internal notes and budgets confidential
Core Concepts
View Definition
A View Definition is a shareable resource that defines how to filter JSON data using GraphQL. It consists of:
GraphQL Schema: Defines the structure and fields available in the source data
GraphQL Query: Specifies which fields to include in the view
Metadata: Name, description, and purpose of the view
View Definitions are stored in a registry and can be reused across multiple resources. They're idempotent by name - creating a View Definition with the same name and content returns the existing definition.
Source Resource and Source Container
Source Resource: A regular Solid JSON resource that contains the original data
Source Container: A regular Solid container with JSON resources
Source Resources are protected while View Bindings exist - attempting to delete a Source Resource with active bindings returns a 409 Conflict error.
View Binding
A View Binding connects a View Definition to Source Resource(s) and specifies where to create the View Resource(s). There are two types:
VIEW_RESOURCE: Filters a single Source Resource, creating one View Resource at an explicit destination URIVIEW_CONTAINER: Filters all JSON resources in a Source Container, creating View Resources in a destination container with matching relative paths
View Bindings are asynchronous - View Resources are created shortly after the binding (typically within 10 seconds).
View Resource and View Container
View Resource: A dynamic, read-only resource containing filtered JSON data from a Source Resource
View Container: A container of View Resources created from a Source Container
View Resources automatically update when source data changes. They can be shared using ACP or Access Grants like regular resources, and support notifications for change tracking.
Relationship Diagram
Data Views Endpoints
By default, the Data Views Service runs from the following root URL:
The Data Views API consists of the following endpoints:
POST /views/registry
Create a new View Definition
GET /views/registry
List all View Definitions (paginated)
GET /views/registry/{id}
Retrieve a specific View Definition
DELETE /views/registry/{id}
Delete a View Definition
POST /views/bindings
Create a View Binding (VIEW_RESOURCE or VIEW_CONTAINER)
POST /views/bindings/preview
Preview filtered data without creating a binding (VIEW_RESOURCE only)
DELETE {viewResource}
Delete a View Resource and its binding
View Registry Endpoints
Create View Definition
Creates a new View Definition in the registry. View Definitions are idempotent by name - submitting the same name with identical content returns the existing definition (201 Created). Submitting the same name with different content returns a conflict error (409).
Input
Endpoint
https://storage.{ESS Domain}/views/registry
Method
POST
Authorization
Access token (DPoP or Bearer) with registry authorization
Content-Type
application/json
Payload
View Definition object
View Definition Structure
type
Required. The query type. Currently only "graphql" is supported (case-insensitive).
name
Required. Human-readable name for the View Definition. Used for idempotency - definitions with the same name and content are deduplicated.
schema
Required. GraphQL Schema Definition Language (SDL) defining the structure of the source data. Must be valid GraphQL SDL syntax.
query
Required. GraphQL query string specifying which fields to include in the view. Must be compatible with the provided schema.
description
Optional. Detailed description of what the View Definition does and its intended use.
purpose
Optional. Short identifier for the View Definition's purpose (e.g., "contact-sharing", "privacy", "reporting").
Example Request
Output
Returns 201 Created with a Location header pointing to the new View Definition URI. The response body contains the complete View Definition including its unique identifier.
Example Response
Validation Rules
Schema validation: The GraphQL schema must be valid SDL syntax. Invalid syntax returns 400 Bad Request.
Query validation: The GraphQL query must be syntactically correct and compatible with the schema. References to fields not in the schema return 400 Bad Request.
Security limits: Queries exceeding maximum depth or complexity limits return 400 Bad Request (see Configuration section).
Idempotency: Same name + same content returns existing definition (201 Created with existing URI).
Name conflicts: Same name + different content returns 409 Conflict.
Authorization Required
Creating and deleting View Definitions requires special registry authorization. Only WebIDs in the registry authorization allow-list can create or delete View Definitions. For configuration details, see the Authorization section.
List View Definitions
Retrieves a paginated list of all View Definitions in the registry. Any authenticated user can list View Definitions.
Input
Endpoint
https://storage.{ESS Domain}/views/registry
Method
GET
Authorization
Access token (DPoP or Bearer) - any authenticated user
Query Parameters
cursor (optional): Pagination cursor for next page
Example Request
Output
Returns 200 OK with a paginated list of View Definitions.
Example Response
Pagination
definitions
Array of View Definition objects for the current page.
hasMore
Boolean indicating if more pages are available.
nextCursor
Opaque cursor string to retrieve the next page. Only present when hasMore is true.
size
Number of definitions in the current page.
To retrieve the next page, include the cursor query parameter:
Retrieve View Definition
Retrieves a specific View Definition by its URI. Any authenticated user can retrieve View Definitions.
Input
Endpoint
https://storage.{ESS Domain}/views/registry/{definitionId}
Method
GET
Authorization
Access token (DPoP or Bearer) - any authenticated user
Example Request
Output
Returns 200 OK with the View Definition, or 404 Not Found if the definition doesn't exist.
Example Response
Delete View Definition
Deletes a View Definition from the registry. Any agent on the registry authorization allow-list can delete View Definitions (requires registry authorization).
Existing Bindings Not Affected
When a View Binding is created, it copies the View Definition's schema and query. Deleting a View Definition from the registry does not affect existing bindings that reference it - those bindings will continue to work with their copied definitions.
Input
Endpoint
https://storage.{ESS Domain}/views/registry/{definitionId}
Method
DELETE
Authorization
Access token (DPoP or Bearer) with registry authorization
Example Request
Output
Returns 204 No Content on successful deletion.
Example Response
View Bindings Endpoints
Preview View Binding
Allows you to test a View Binding before creating it by returning the filtered data immediately. This is useful for validating that your View Definition produces the expected output.
VIEW_RESOURCE Only
Preview is only available for VIEW_RESOURCE bindings. Attempting to preview a VIEW_CONTAINER binding returns 400 Bad Request. For VIEW_CONTAINER, create the binding and inspect individual View Resources after async processing completes.
Input
Endpoint
https://storage.{ESS Domain}/views/bindings/preview
Method
POST
Authorization
Access token (DPoP or Bearer) with data subject permission for the Source Resource's storage
Content-Type
application/json
Payload
View Binding request object
Example Request
Output
Returns 200 OK with the filtered JSON data, or 400 Bad Request for validation errors.
Example Response
Assuming the Source Resource contains:
The preview response would be:
Create View Binding
Creates a View Binding that connects a View Definition to Source Resource(s) and produces View Resource(s) at the specified destination. There are two types of View Bindings: VIEW_RESOURCE and VIEW_CONTAINER.
Input
Endpoint
https://storage.{ESS Domain}/views/bindings
Method
POST
Authorization
Access token (DPoP or Bearer) with data subject permission for the Source Resource's storage
Content-Type
application/json
Payload
View Binding object
View Binding Types Comparison
Aspect
VIEW_RESOURCE
VIEW_CONTAINER
Purpose
Filter a single Source Resource
Filter all JSON resources in a container
Source URI
Must NOT end with /
Must end with / (container)
Destination URI
Must NOT end with /
Must end with / (container)
Result
One View Resource at explicit URI
Multiple View Resources with matching relative paths
Preview Support
Yes, via /bindings/preview
No preview available
Data Matching
N/A (single resource)
Only creates views for data that produces non-empty results
VIEW_RESOURCE Binding Structure
VIEW_RESOURCE Binding Structuretype
Required. Must be "VIEW_RESOURCE".
definitionUri
Required. URI of the View Definition to apply. Must be a valid, existing View Definition in the registry.
sourceResource
Required. URI of the source JSON resource to filter. Must NOT end with /. Must be a resource you have data subject permission for.
destinationResource
Required. URI where the View Resource will be created. Must NOT end with /. Can be any valid URI in your storage.
Example VIEW_RESOURCE Request
VIEW_RESOURCE RequestExample VIEW_RESOURCE Response
VIEW_RESOURCE ResponseVIEW_CONTAINER Binding Structure
VIEW_CONTAINER Binding Structuretype
Required. Must be "VIEW_CONTAINER".
definitionUri
Required. URI of the View Definition to apply to all matching resources in the Source Container.
sourceResource
Required. URI of the Source Container. Must end with /. All JSON resources in this container and its subcontainers will be processed.
destinationResource
Required. URI of the destination container where View Resources will be created. Must end with /. View Resources will maintain the same relative path structure as Source Resources.
Example VIEW_CONTAINER Request
VIEW_CONTAINER RequestExample VIEW_CONTAINER Response
VIEW_CONTAINER ResponseVIEW_CONTAINER Path Structure
VIEW_CONTAINER Path StructureWhen a VIEW_CONTAINER binding is created, View Resources are created with the same relative paths as their Source Resources:
Asynchronous Processing
View Binding creation is asynchronous. The API returns 201 Created immediately, but View Resources are materialized shortly afterward (typically within 10 seconds). Use retry logic when accessing View Resources to account for processing time. See Asynchronous Processing for details.
Idempotency
View bindings are idempotent by destination, definition, and source. Submitting the same binding request multiple times succeeds (201 Created) and references the same view resource.
Delete View Resource
Deletes a View Resource and its corresponding binding. If there are no more bindings on the associated Source Resource, this also removes its protection, allowing it to be deleted.
Input
Endpoint
{viewResourceUri}
Method
DELETE
Authorization
Access token (DPoP or Bearer) with write permission for the View Resource
Example Request
Output
Returns 204 No Content on successful deletion.
Example Response
GraphQL Query Features
The Data Views API uses GraphQL to define how source data is filtered. This section explains the supported GraphQL features for creating View Definitions.
Supported Data Types
Standard Scalars
GraphQL's standard scalar types are fully supported:
String: Text valuesInt: 32-bit integer valuesFloat: Floating-point numeric valuesBoolean: True or false valuesID: Unique identifier values (treated as strings)
Extended Scalars
Data Views supports additional scalar types for common data formats:
DateTime: ISO-8601 formatted date-time values (e.g.,"2024-03-15T10:30:00.000Z")Json: Arbitrary JSON objects or arrays
Extended scalars must be declared in the schema:
Field Selection
The simplest use of GraphQL is selecting specific fields from source data.
Basic Field Selection
Schema:
Query:
Source Data:
View Result:
Nested Object Selection
Schema:
Query:
Source Data:
View Result:
Array Field Selection
Schema:
Query:
Source Data:
View Result:
Query Filters
GraphQL filters allow you to select specific items from arrays based on conditions. Filters are specified as arguments to fields.
Equality Filters
Filter array items where a field equals a specific value:
Schema:
Query:
Source Data:
View Result:
Numeric Comparison Filters
Filter numeric fields using comparison operators:
eq
Equal to
gt
Greater than
gte
Greater than or equal to
lt
Less than
lte
Less than or equal to
Schema:
Query (transactions over $100):
Source Data:
View Result:
Query (transactions between $50 and $200):
Array Membership Filters
Filter items where a field value is in a specified set.
Schema:
Query:
Source Data:
View Result:
Date Range Filters
Filter DateTime fields using temporal comparisons:
after
DateTime is after (exclusive) the specified value
before
DateTime is before (exclusive) the specified value
Schema:
Query (March 2024 transactions):
Source Data:
View Result:
Multiple Filter Combination
Multiple filters on the same field are combined with AND logic.
Schema:
Query (Acme Inc purchases over $100 in March 2024):
All specified filters must match for a transaction to be included in the View.
Complete Workflows
Workflow 1: Basic Contact Filtering (VIEW_RESOURCE)
VIEW_RESOURCE)This workflow demonstrates creating a filtered View of a single resource containing personal contact information.
Step 1: Create Source Resource
Create a JSON resource with complete contact information:
Response:
Step 2: Create View Definition
Create a View Definition that filters to only name and email. Note that the schema only needs to define the fields used in the query - it doesn't need to include all fields from the Source Resource:
Response:
Step 3: Preview the Binding (Optional)
Test the View Definition before creating the binding:
Response:
The preview confirms that phone, address, and ssn are filtered out as expected.
Step 4: Create View Binding
Create the binding to produce the View Resource:
Response:
Step 5: Access View Resource
Access the View Resource (with retry for async processing):
Initial response (if processing not complete):
Retry after a few seconds.
Response (after processing completes):
Step 6: Share View Resource
Now you can share the View Resource using ACP or Access Grants. External users will only see name and email. They will never see the phone, address, or SSN fields from the Source Resource.
Step 7: Update Source Resource
Update the Source Resource with new data:
Response:
Step 8: View Auto-Updates
After a short delay, the View Resource automatically reflects the changes:
Response:
The View Resource updated automatically. Note that phone and address changes are not visible in the view.
Step 9: Delete View Binding
To stop sharing and remove the View Resource:
Response:
After a short delay, the View Resource is deleted and accessing it returns 404 Not Found. The Source Resource remains unchanged and can now be deleted if needed.
Workflow 2: Bulk Transaction Filtering (VIEW_CONTAINER)
VIEW_CONTAINER)This workflow demonstrates creating filtered Views of multiple resources in a container.
Step 1: Create Source Container
Create a container for transaction records:
Response:
Step 2: Add Transaction Resources
Add multiple transaction records to the container.
January Transactions:
February Transactions:
Responses:
Step 3: Create View Definition for Transactions
Create a View Definition that filters out account numbers:
Response:
Step 4: Create VIEW_CONTAINER Binding
VIEW_CONTAINER BindingCreate a Binding for the entire container:
Response:
Step 5: Access View Resources
After async processing completes, access the filtered View Resources:
January View:
Response:
February View:
Response:
Note that accountNumber is filtered out from all View Resources.
Step 6: Add New Source Resource
Add a new transaction file to the Source Container:
Response:
Step 7: New View Auto-Created
After async processing, a View Resource is automatically created for the new file:
Response:
Step 8: Delete Source Resource
Delete one of the Source Resources:
Response:
Step 9: Corresponding View Auto-Deleted
After async processing, the corresponding View Resource is automatically deleted:
Response:
The other View Resources (2024-02.json and 2024-03.json) remain available.
Key Behaviors and Constraints
Asynchronous Processing
All View Operations Are Asynchronous
View resource creation, updates, and deletions happen asynchronously after API operations complete. The API returns success immediately, but materialization occurs shortly afterward.
Typical Processing Times
View Resource creation: 5-15 seconds after binding creation
View Resource updates: 5-15 seconds after Source Resource changes
View Resource deletion: 5-15 seconds after binding deletion
Actual times depend on system load and data size.
Client Retry Logic
Due to the asynchronous nature of View Resource creation and deletion, clients should implement retry logic when accessing newly created View Resources or verifying deletion completion.
Source Protection
Source root resources cannot be deleted while bindings exist
Only the source root resource (for VIEW_RESOURCE bindings) or source root container (for VIEW_CONTAINER bindings) is protected from deletion while active bindings exist. Attempting to delete a protected source returns a 409 Conflict error.
VIEW_RESOURCE: The specific Source Resource referenced in the binding cannot be deletedVIEW_CONTAINER: The Source Container itself cannot be deleted, but nested child resources within the container can be deleted (which will trigger automatic deletion of their corresponding View Resources)
Protected Deletion Example - VIEW_RESOURCE
VIEW_RESOURCEResponse (when View Binding exists):
Protected Deletion Example - VIEW_CONTAINER
VIEW_CONTAINERWhen a VIEW_CONTAINER binding exists for https://storage.example.com/alice/transactions/:
Attempting to delete the Source Container (BLOCKED):
Response:
Deleting a nested child resource (ALLOWED):
Response:
The nested child resource is deleted successfully, and the corresponding View Resource at /alice/views/transactions/2024-01.json is automatically deleted asynchronously.
Discovering View Bindings
To identify View Bindings on a Source Resource, inspect the Link headers in the resource's HTTP response. Source Resources with active bindings include a Link header with the relation type https://w3id.org/inrupt/namespace/vocab/storage/hasViewResource.
Example Request:
Example Response:
In this example, the Source Resource has two View Bindings pointing to:
https://storage.example.com/alice/shared/contact-viewhttps://storage.example.com/alice/public/basic-info
These URIs identify the View Resources that must be deleted before the source can be deleted.
Resolution
To delete a protected Source Root Resource or Container:
Discover View Bindings: Inspect the
Linkheaders on the Source Resource to identify all View ResourcesDelete each View Resource: Send DELETE requests to each View Resource URI (which deletes the binding)
Delete the source: Once all bindings are removed, delete the source root resource or container
Step 1: Discover bindings
Response shows two View Bindings in the Link headers.
Step 2: Delete View Resources
Step 3: Delete Source Resource
After all View Resources are deleted, the source can be deleted successfully:
Response:
Idempotency
View Definition Idempotency
View Definitions are idempotent by name and content:
Scenario 1: Identical Name and Content
First request response:
Repeat request with identical content:
The same Definition URI is returned. No duplicate is created.
Scenario 2: Same Name, Different Content
Response:
View Binding Idempotency
View Bindings are idempotent by destination, definition, and source:
First request:
Repeat request:
The same Binding is returned. No duplicate is created.
Data Matching
VIEW_CONTAINER Filtering Behavior
VIEW_CONTAINER Filtering BehaviorWhen a VIEW_CONTAINER binding is created, not all Source Resources necessarily produce View Resources. The View Definition's GraphQL query acts as a filter.
Resources That Produce Views:
JSON resources where the query produces non-empty results
Resources That Don't Produce Views:
Non-JSON resources (text files, images, etc.)
JSON resources where the query produces empty results (no matching data)
Resources with malformed JSON
Resources with data that doesn't match the schema
Example: Selective View Creation
Source Container:
View Definition:
Result in View Container:
Non-JSON Source Resources
Behavior with Non-JSON Resources
When a View Binding references a non-JSON Source Resource:
VIEW_RESOURCE:
Binding creation succeeds (201 Created)
View resource is NOT created
Accessing the View Resource URI returns 404 Not Found
VIEW_CONTAINER:
Binding creation succeeds (201 Created)
Non-JSON resources are silently skipped
Only JSON resources that match the query produce View Resources
Example: Text File Source
Response:
Accessing the view:
Response:
The binding exists but no View Resource is materialized because the source is not JSON.
Authorization
Authentication
All Data Views API requests require authentication using either DPoP (Demonstrating Proof-of-Possession) or Bearer tokens.
For details on authentication methods, see Authentication in Solid.
View Registry Authorization
Read Operations (GET)
GET)Any authenticated user can list and retrieve View Definitions:
Response:
Write Operations (POST, DELETE)
Creating and deleting View Definitions requires special registry authorization. Only WebIDs in the registry authorization allow-list can perform these operations.
Authorized Request:
Response:
Unauthorized Request:
Response:
Configuration
Registry authorization is configured via environment variables or application properties. Only ESS Administrators are allowed to edit the allow-list. See Registry Authorization Configuration for details.
View Binding Authorization
Creating Bindings
To create a View Binding, you must have data subject permission for the storage containing the Source Resource. This means you must be the owner of the Pod where the Source Resource resides.
Authorized Request (own Storage):
Response:
Unauthorized Request (not data subject):
Response:
Accessing View Resources
View Resources are regular Solid resources and respect standard access control:
ACP Policies: Apply ACP policies to View Resources to control access
Access Grants: Issue Access Grants for View Resources
Read Permission: Users need read permission to access View Resources
View Resources do NOT inherit access control from Source Resources. You must explicitly grant access to View Resources.
Error Responses
The Data Views API uses RFC 7807 Problem Details format for error responses.
View Definition Errors
Invalid GraphQL Schema
Response:
Query-Schema Mismatch
Response:
View Definition Name Conflict
Response:
Exceeds Security Limits
Response:
View Binding Errors
Missing Required Fields
Response:
Source and Destination on Different Storages
Response:
Invalid Binding Type
Response:
VIEW_CONTAINER URI Requirements
VIEW_CONTAINER URI RequirementsResponse:
Non-Existent View Definition
Response:
Source Resource Not Found
Response:
Authorization Errors
Insufficient Registry Authorization
Response:
Insufficient Data Subject Permission
When the source and destination are on the same storage but the agent making the request is not the data subject:
Response:
Configuration
The Data Views Service is configured via environment variables or application properties files. Only ESS Administrators can adjust these settings.
Security Limits
These settings control GraphQL query complexity to prevent resource exhaustion attacks.
Maximum Query Depth
Limits the maximum nesting depth of GraphQL queries.
Default: 10
Example: A query with depth 12 would be rejected:
Maximum Query Complexity
Limits the total complexity score of GraphQL queries. Complexity is calculated based on field count and nesting.
Default: 1000
Queries with complexity scores exceeding this limit are rejected with a 400 Bad Request error.
Maximum List Size
Limits the maximum number of items that can be processed in array filtering operations.
Default: 10000
If a Source Resource contains an array with more than this many items, the query is rejected.
GraphQL Schema Caching
View Definitions' GraphQL schemas are parsed and cached to improve performance. These settings control cache behavior.
Cache Maximum Size
Maximum number of parsed schemas to keep in cache.
Default: 100
When the cache reaches this size, least-recently-used schemas are evicted.
Cache Expiry Time
How long parsed schemas remain in cache after last access.
Default: 1h (1 hour)
Valid values: Time duration string (e.g., 30m, 2h, 1d)
After this time, schemas are evicted and must be re-parsed on next use.
Registry Authorization
Controls which WebIDs are authorized to create and delete view definitions in the registry. Only WebIDs in the allow-list can perform write operations (POST, DELETE) on /views/registry. Read operations (GET) are available to all authenticated users.
Allow List Configuration
Specify authorized WebIDs using a comma-separated list:
Default: Empty (no users authorized for write operations)
Format: Comma-separated list of WebID URIs
Example with multiple WebIDs:
Authorization Required for Write Operations
If the allow-list is empty or not configured, all attempts to create or delete View Definitions will return 403 Forbidden. Ensure at least one WebID is configured to enable View Definition management.
Additional Information
Related Concepts
Access Control Policies (ACP): Use ACP to control who can access View Resources
Access Grants: Issue Access Grants for View Resources to enable consent-based data sharing
Notifications: Subscribe to notifications for View Resources to track changes
Auditing Service: View operations generate audit events for compliance
Best Practices
Security
Minimize View Definitions: Create View Definitions that expose only necessary fields, following the principle of least privilege
Review GraphQL queries: Ensure queries don't inadvertently expose sensitive data through complex filtering logic
Use short-lived Access Grants: When sharing View Resources, use Access Grants with appropriate expiration times
Monitor audit logs: Regularly review audit events for view operations to detect unauthorized access attempts
Performance
Reuse View Definitions: Create generic View Definitions that can be applied to multiple resources instead of creating specific definitions for each resource
Limit query complexity: Keep GraphQL queries simple to reduce processing overhead
Use
VIEW_CONTAINERsparingly:VIEW_CONTAINERbindings process all resources in a container, which can be resource-intensive for large containers
Data Management
Document View Definitions: Include clear descriptions and purposes for View Definitions to help future maintenance
Plan destination URIs: Use consistent naming conventions for View Resources to make them easy to discover and manage
Clean up unused bindings: Regularly delete View Bindings that are no longer needed to free resources and simplify management
Development Workflow
Use preview before binding: Always preview
VIEW_RESOURCEbindings before creating them to verify the output matches expectationsTest with sample data: Create View Definitions using sample data that includes edge cases (null values, empty arrays, malformed dates)
Implement retry logic: Always use retry logic when accessing View Resources to account for asynchronous processing
Further Reading
GraphQL Language: https://graphql.org/learn/
GraphQL Schema Definition: https://graphql.org/learn/schema/
Quarkus Configuration: https://quarkus.io/guides/all-config
DPoP Specification: RFC 9449 - OAuth 2.0 Demonstrating Proof of Possession
RFC 7807 Problem Details: https://www.rfc-editor.org/rfc/rfc7807.html
Last updated