Library
FD1 UserInterface Endpoint
Manages the description of url paths to content, providing instructions on how a url path ( /my-example.html ) might be rendered. Responses may be actual HTML/etc content, or supporting information, such as how an edit-customers page might appear to users.
Anatomy of a web request
-
Content Fetch
- Classic "request a URL", get a response. HTML, CSS, JS, Image
-
Breadcrumb Resolve
- Converts well known paths or tokens to actual underlying URLs. This allows a well known path, such as /products/main_menu to be aliased to the actual URL structure used. In this case /products/main_menu might really be the path /menus/products_V14.2.html
-
Render Support
- Additional customisation information, when the content page is a generic file that can adapt and appear differently depending run time config.
Worked Example
Your website for use is https://instore.xyz being a web server inside your VPN. This might use https://retailer456.example.com for all data and processing. So instore.xyz contains all the user interface, while retailer456.example.com is a Cloud server providing data APIs only.
A staff member browses to https://instore.xyz/home.htm This is a content fetch. The response might be something like this
<HTML> ... <a href='/fd1/userinterface.link.htm?k=products_edit'>Edit Products</a> <a href='/fd1/userinterface.link.htm?k=products_new'>Add New Product</a> ...The '/fd1/userinterface.link.htm?k=products_edit' are breadcrumb resolves. That path is a not a real URL in the classic sense, rather it is a well known path, that will redirect to the actual URL scheme
The breadcrumb resolve might redirect to /store/australia/generic_edit.htm?_$table=products This is a generic page, that alters its behaviour at runtime. The page itself then calls to fd1.userinterface to request render support information. A json reply is received that supplies underlying html and scripts to become a product edit page
Thats the simple version, a few things to note
- This whole userinterface system is optional, you are free to ignore it and create your own User Interface, and simply open a websocket to read/write data from your data host.
-
When loading content fetch you have several options
- Files are loaded from a directory on disk as any web server does
- Files are loaded from database records. ie the web page html is stored inside the database
- The server can silently request the URL from a different server and return that result to the user. This allows your site instore.xyz to silently use the site https://franchise-parent.com as a library, but without the user seeing the connection.
- When the content page is a generic page the url the user sees can be either /store/australia/generic_edit.htm OR /my-edits/products.htn - in the second case the server silently returns the HTML from /store/australia/generic_edit.htm
- A generic page is simply a normal HTML web page that calls fd1.userinterface.get_render to load the actual appearance to use. This two step means that you can use AI tools (or hand code if you wish) to alter a web page without gettting into deep HTML internals. A generic page supports an "engine", which understands different render information. You could of course just have the AI write the complete page and serve that as content, and that is valid for high use pages, but generic pages are great for rapid deployment, and rarely used pages (eg edit payment types might only be called once a year)
fd1.userinterface.link.htm
Returns a final target URL for a given input URL, providing mapping from one namespace to another. It is most helpful for menu links, where the page to be displayed can vary based on user, location and even device being used. A user on an ordering iPad wanting the "products" menu page probably has a different range of options to a product controller. With userinterface.link you can provide different URL redirects based on context.
Warning. This endpoint behaves differently based on how it is called.
You do not have to use this method of dynamically changing webpage content, there are other options, however this method can be helpful if creating a complete custom application website
Example. To create menu links on a page
... We are on a detail page, say single_product_info.htm ...
<menu>
<a href='/fd1/userinterface.link.htm?k=products'>Products Menu</a>
</menu>
When this link is clicked, the web server will receive a navigation request for '/fd1/userinterface.link.htm?k=products'. If the webserver understands this URL scheme, then it will return the HTML, or 302 redirect, or other valid HTTP responses.
Fields
| field name | get_content | get_resolve | get_render | insert | edit | read | Datatype | Description |
| id | R (reqd) | R | string | Server allocated unique row primary key | ||||
| table | R | RW | RW | R | string | Optional database table this page deals with. Typically used for edit "xyz" type pages. | ||
| path | R | RW | RW | R | string | URL Path this entry is defining | ||
| origin | R | RW | RW | R | string | URL Origin this entry is defining | ||
| kind | R | RW | RW | R | string enum | What type of entry this is. [ content | content.fetch | resolve | edit-record | insert-record | delete-record ] | ||
| designed_at | R | datetime UTC | ||||||
| lang | R | RW | RW | R | string | |||
| content | R | R | RW | RW | R | blob | ||
| contenttype | R | R | RW | RW | R | string | ||
| httpresponse | R | R | RW | RW | R | number | ||
| engine | R | RW | RW | R | string enum | |||
| enginedata | R | RW | RW | R | object | |||
| enginecomments | R | RW | RW | R | string | |||
| rve | R | R | double | |||||
| _unknowns_ | R ? | R | object | Fields that were seen using insert/edit but not valid at that time. Rather than rejecting, fields are saved as unknowns. | ||||
| enginereference | R | string url | Server supplied URL to documentation describing the engine being used. Intended for AI/LLM to auto learn if changing the page | |||||
| meta | R | object | Current known fields for "table" on the server. This is provided as an optimisaton |
fd1.userinterface.get_content
Fetch a URL contents given input path, origin.
- When called over a websocket the response is a JSON packet.
- When called as a direct URL, the response is valid HTTP.
- The associated webserver MAY translate URLs directly to get_content calls in the background.
fd1.userinterface.get_resolve
fd1.userinterface.get_render
Looks up and returns the specific details for a given origin, path and query.
Many web pages use a standard template .html file, and this html file requests specific details for its current URL and user. This is then rendered via the page itself.
Example
curl -H "apikey: ..??.." "https://example.com/fd1/userinterface/get_render?_v.origin=https://my-cdn.com&_v.path=/editcustomer1.htm"
fd1.userinterface.insert
Add a new page rule to the database. This single endpoint handles all inserts regardless of 'kind'. Some fields are only valid for specific 'kind'.
You can provide an idempotency key using "vk" field. See protocol.htm. If vk is not specified, the server will generate one based on fields in the payload
Adding Content
Uploading a html, css, js file to be served
{
a: "fd1.userinterface.insert",
v: {
origin: "https://example.com",
path: "/index.html",
kind: "content",
content: "<HTML> ...",
contenttype: "text/html",
httpresponse: 200
}
}
Adding Remote Content
Instructing that all resources from a path should be fetched from a third party server. In this example, browsing to https://example.com/franchisereports/monthend.htm will cause the server to fetch https://franchisor.com/some/path/monthend.htm and return that result as if it was locally present.
{
a: "fd1.userinterface.insert",
v: {
origin: "https://example.com",
path: "/franchisereports/*",
kind: "content.fetch",
enginedata: {
source: "https://franchisor.com/some/path/*",
headers: {
x-api-key: "secret"
}
},
httpresponse: 404
}
}
Adding Local File Content
Having the webserver return local files as web resources cannot be done via the API for security reasons. To allow this mode of operation, you will need to manually edit the webserver ini file.
fd1.userinterface.edit
fd1.userinterface.read
Reads the underlying full structure for a page Id. This is the 'database record' as might be required by tools that wish to edit the definition. When requesting for "use", call fd1.userinterface.get. This .read endpoint may be security restricted, while .get is designed to be the "get for rendering purposes"
A good rule of thumb is if you are running a web page, you have origin, path, query, then fd1.userinterface.get is what you need. If you are designing or wanting to access by id - use fd1.userinterface.read
Example
Reading id 83EDB0C4PP.51895000.1355626984 over a websocket
{
a: "fd1.userinterface.read",
v: {
id: "83EDB0C4PP.51895000.1355626984"
}
}
Or the equivalent using CURL
curl -H "apikey: ..???.." "https://example.com/fd1/userinterface/read?_v.id=83EDB0C4PP.51895000.1355626984"
Getting full history of changes
{
a: "fd1.userinterface.read",
qk: "history",
v: {
id: "83EDB0C4PP.51895000.1355626984"
}
}
curl -H "apikey: ..???.." "https://example.com/fd1/userinterface/read?_v.id=83EDB0C4PP.51895000.1355626984&_qk=history"
Example Response
{
"r": "fd1.userinterface.read",
"data": {
"id": "83EDB0C4PP.51895000.1355626984",
"table": "customers",
"path": "/editcustomer3.htm",
"enginedata": {
"subobj": {
"a": 6,
"b": "c"
},
"scripts": [
"s1",
"s2"
],
"css": "std",
"html": "<p>Hello World</p>"
}
}
}
Example Reponse - History
Shows the flow of transactions that built the current record version. In this example there is an "invalidate" entry for the sake of completeness. These are exceedingly rare, and only exist where a secondary process has reverted an edit - such as a bad bulk API upload, invalidate removes them all
{
"r": "fd1.userinterface.read",
"data": {
"rows": [
{
"action": "invalidate",
"dtu_stored": "2026-08-01",
"dtu_txn": "2026-08-01"
},
{
"action": "edit",
"dtu_stored": "2026-07-31 21:43:30.549",
"dtu_txn": "2026-07-31 21:43:30.549",
"data": {
"id": "83EDB0C4PP.51895000.1355626984",
"enginedata": {
"subobj": {
"a": 6,
"b": "c"
}
}
}
},
{
"action": "edit",
"dtu_stored": "2026-07-31 21:42:18.067",
"dtu_txn": "2026-07-31 21:42:18.067",
"data": {
"id": "83EDB0C4PP.51895000.1355626984",
"enginedata": {
"scripts": [
"s1",
"s2"
]
}
}
},
{
"action": "edit",
"dtu_stored": "2026-07-31 21:14:48.494",
"dtu_txn": "2026-07-31 21:14:48.494",
"data": {
"id": "83EDB0C4PP.51895000.1355626984",
"enginedata": {
"css": "std"
}
}
},
{
"action": "edit",
"dtu_stored": "2026-07-31 20:48:26.499",
"dtu_txn": "2026-07-31 20:48:26.499",
"data": {
"id": "83EDB0C4PP.51895000.1355626984",
"table": "customers",
"path": "/editcustomer3.htm",
"enginedata": {
"html": "<p>Hello World</p>"
}
}
},
{
"action": "edit",
"dtu_stored": "2026-07-31 20:48:00.171",
"dtu_txn": "2026-07-31 20:48:00.171",
"invalidated": "2026-08-01",
"data_invalidated": {
"id": "83EDB0C4PP.51895000.1355626984",
"table": "customers",
"path": "/editcustomer.htm",
"enginedata": {
"html": ""
}
}
},
{
"action": "insert",
"dtu_stored": "2026-07-31 20:47:06.262",
"dtu_txn": "2026-07-31 20:47:06.262",
"data": {
"table": "customers",
"path": "/editcustomer.htm",
"enginedata": {
"html": "<p>Hello Mars</p>"
},
"id": "83EDB0C4PP.51895000.1355626984"
}
}
]
}
}