npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gov-cy/govcy-express-services

v1.7.2

Published

An Express-based system that dynamically renders services using @gov-cy/govcy-frontend-renderer and posts data to a submission API.

Readme

govcy Express Services

npm (scoped) License Unit test tag-and-publish-on-version-change coverage

⚠️ Warning:
No guarantees are provided regarding stability, security, or compliance. Using this package does not imply your product or service will automatically pass any required assessments, audits, or certifications by the Cyprus government or any other authority.

You are responsible for ensuring your own compliance, security, and quality assurance processes.

📝 Description

This project is an Express-based project that dynamically renders online service forms using @gov-cy/govcy-frontend-renderer, handles data input, validations, renders a review page and submits the data via a submission API. It is designed for developers building government services in Cyprus, enabling them to manage user authentication, form submissions, and OpenID authentication workflows in a timely manner.

The project is designed to support the Linear structure as described in the Unified Design System.

The APIs used for submission, temporary save and file uploads are not part of this project. The project has been designed to work together with the DSF Submission plarform and all API calls are based on the DSF Submission Platform APIs. This readme file describes the definition of these APIs if you wish to develop your own for your own government back-end solution. For more details about the DSF Submission Platform contact the DSF team.

govcy-express-services

Table of contents

✨ Features

  • Dynamic form rendering from JSON templates
    • Support for textInput, textArea, select, radios, checkboxes, datePicker, dateInput, fileInput elements
    • Support for conditional radios
  • Dynamic creation of check your answers page
  • OpenID Connect authentication with CY Login
  • Middleware-based architecture for better maintainability
  • Supports routing for dynamic pages
  • Input validation
  • CSRF protection
  • cyLogin Single Sign-On (SSO) for physical authorized users
  • Pre-filling posted values (in the same session)
  • Site level API eligibility checks
  • API integration with retry logic for form submissions.
  • Optional temporary save of in-progress form data via configurable API endpoints
  • Optional file uploads via API endpoints

📋 Prerequisites

  • Node.js 20+
  • npm
  • A CY Login client ID and secret
  • An API endpoint for form submissions (through cyConnect)

🚀 Quick start

# 1. Install the package
npm install @gov-cy/govcy-express-services

# 2. Generate SSL certificates for local development
openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.cert -days 365 -nodes

# 3. Create a `secrets/.env` file in your project (see below for required variables)

# 4. Add a minimal data config file in /data (see test.json example)

# 5. Create an index.mjs file:
// index.mjs
import initializeGovCyExpressService from '@gov-cy/govcy-express-services';

const service = initializeGovCyExpressService();
service.startServer();
# 6. Start the server
npm start

Tip:
For more details on configuration, environment variables, and advanced features, see the sections below.

📦 Full installation guide

The project acts as an npm package and you need to install it as a dependency in your npm project. Check out the install notes a detailed installation guide.

✅ Best Practices

Before starting your service, please review the Best Practices guide for guidance on:

  • Repository structure
  • Environment separation (dev / staging / prod)
  • Secure CY Login client registration
  • Mandatory footer pages (privacy, cookies, accessibility)

🛠️ Usage

Starting the Server

Add in your package.json:

"scripts": {
    "start": "node index.mjs"
}

Then run the server using npm start.

npm start

The server will start on https://localhost:44319 (see NOTES.md for more details on this).

🔑 Authentication Middleware

Authentication is handled via OpenID Connect using CY Login and is configured using environment variables. The middleware ensures users have valid sessions before accessing protected routes.

The CY Login tokens are used to also connect with the various APIs through cyConnect, so make sure to include the correct scope when requesting for a cyLogin client registration.

The CY Login settings are configured in the secrets/.env file.

cyLogin Access Policies

Each service can specify which types of authenticated CY Login profiles are allowed to access it using the site.cyLoginPolicies property in its site configuration.

"cyLoginPolicies": ["naturalPerson", "legalPerson"]
Supported Policies

| Policy name | Description | Typical use | | --------------- | ------------------------------------------------------------ | ---------------------------------------------------- | | naturalPerson | Allows individual users (Cypriot citizens or foreign residents) who have a verified profile in the Civil Registry. Identified by profile_type: "Individual" and a 10-digit identifier starting with 00 (citizen) or 05 (foreigner). | Citizen-facing services, personal applications, etc. | | legalPerson | Allows legal entities (companies, partnerships, organisations) with verified profiles in the Registrar of Companies. Identified by profile_type: "Organisation" and a legal_unique_identifier. | Business-facing services, company submissions, etc. |

How it works
  • Access is granted if any of the listed policies pass.
  • If the user’s CY Login profile does not match any of the allowed policies, the request is blocked.
Defaults

If cyLoginPolicies is omitted, the framework defaults to:

"cyLoginPolicies": ["naturalPerson"]

This maintains backward compatibility with existing services that only supported individual (civil registry) users.

Example

Allow both natural and legal persons:

"site": {
  "cyLoginPolicies": ["naturalPerson", "legalPerson"]
}

Restrict access to natural persons only:

"site": {
  "cyLoginPolicies": ["naturalPerson"]
}
Notes
  • This configuration applies globally to the service.
  • Both requireAuth and cyLoginPolicy middlewares must be present on protected routes (automatically included by the default route setup).

🧩 Dynamic Services

Services are rendered dynamically using JSON templates stored in the /data folder. All the service configuration, pages, routes, and logic is stored in the JSON files. The service will load data/:siteId.json to get the form data when a user visits /:siteId/:pageUrl. Checkout the express-service-shema.json and the example JSON structure of the test.json file for more details.

Here is an example JSON config:

{
  "site": {
    "id": "test",
    "usesDSFSubmissionPlatform": true,  //<-- Indicates whether the service uses the DSF submission platform (transforms submission data as needed)
    "cyLoginPolicies": ["naturalPerson"], //<-- Allowed CY Login policies
    "lang": "el",     //<-- Default language
    "languages": [    //<-- Supported languages
      {
        "code": "el",
        "label": "EL",
        "alt": "Ελληνική γλώσσα",
        "href": "?lang=el"
      },
      {
        "code": "en",
        "label": "EN",
        "alt": "English language",
        "href": "?lang=en"
      }
    ],
    "footerLinks": [ //<-- Links on the footer
      {
        "label": {
          "el": "Δήλωση απορρήτου",
          "en": "Privacy statement",
          "tr": "Privacy statement"
        },
        "href": "test/privacy-statement"
      },
      {
        "label": {
          "el": "Cookies",
          "en": "Cookies",
          "tr": "Cookies"
        },
        "href": "test/cookie-policy"
      },
      {
        "label": {
          "el": "Προσβασιμότητα",
          "en": "Accessibility",
          "tr": "Accessibility"
        },
        "href": "test/accessibility-statement"
      }
    ],
    "footerIcons": [ //<-- Icons on the footer
      {
        "target": "_blank",
        "src": {
          "el": "https://cdn.jsdelivr.net/gh/gov-cy/govdesign@main/FundedbyEU_NextGeneration_H53-EL.png",
          "en": "https://cdn.jsdelivr.net/gh/gov-cy/govdesign@main/FundedbyEU_NextGeneration_H53-EN.png",
          "tr": "https://cdn.jsdelivr.net/gh/gov-cy/govdesign@main/FundedbyEU_NextGeneration_H53-EN.png"
        },
        "alt": {
          "el": "Χρηματοδοτείται από την ΕΕ Next Generation EU",
          "en": "Funded by the EU Next Generation EU",
          "tr": "Funded by the EU Next Generation EU"
        },
        "href": {
          "el": "https://europa.eu/",
          "en": "https://europa.eu/",
          "tr": "https://europa.eu/"
        },
        "title": {
          "el": "Μετάβαση στην ιστοσελίδα της ΕΕ",
          "en": "Go to EU website",
          "tr": "Go to EU website"
        }
      },
      {
        "target": "_blank",
        "src": {
          "el": "https://cdn.jsdelivr.net/gh/gov-cy/govdesign@main/CYpros%20to%20aurio%20logo%20eng_H53_EL.png",
          "en": "https://cdn.jsdelivr.net/gh/gov-cy/govdesign@main/CYpros%20to%20aurio%20logo%20eng_H53_EN.png",
          "tr": "https://cdn.jsdelivr.net/gh/gov-cy/govdesign@main/CYpros%20to%20aurio%20logo%20eng_H53_EN.png"
        },
        "alt": {
          "el": "Κύπρος το Αύριο, σχέδιο ανάκαμψης και ανθεντικότητας",
          "en": "Cyprus tomorrow, recovery and resilience plan",
          "tr": "Cyprus tomorrow, recovery and resilience plan"
        },
        "href": {
          "el": "http://www.cyprus-tomorrow.gov.cy/",
          "en": "http://www.cyprus-tomorrow.gov.cy/",
          "tr": "http://www.cyprus-tomorrow.gov.cy/"
        },
        "title": {
          "el": "Μετάβαση στην ιστοσελίδα Κύπρος το Αύριο",
          "en": "Go to Cyprus Tomorrow website",
          "tr": "Go to Cyprus Tomorrow website"
        }
      }
    ],
    "menu": {     //<-- Menu altext
      "el": "Μενού",
      "en": "Menu",
      "tr": "Menu"
    },
    "title": {  //<-- Service title (meta)
      "el": "Υπηρεσία τεστ",
      "en": "Test service",
      "tr": ""
    },
    "headerTitle": {  // <-- The header title settings
      "title": {      //<-- Service title (as it apears in the header)
        "el": "[Το ΟΝΟΜΑ της υπηρεσίας που θα φαίνεται στις φόρμες]",
        "en": "[The NAME of the service as it will appear on forms]",
        "tr": ""
      },
      "href": {       // <-- The relative URL of the header title link (for each language)
          "el":"/service-id",
          "en":"/service-id",
          "tr":"/service-id"
      }
    },
    "reviewPageHeader": {  //<-- OPTIONAL - Review page header. Useful when directing users to the review page from gov.cy start page
      "el": "Υπηρεσία τεστ",
      "en": "Test service",
      "tr": "Test service"
    },
    "successPageHeader": { //<-- OPTIONAL - Success page header
      "el": "Έχουμε λάβει την προσφορά σας",
      "en": "We have received your offer",
      "tr": "We have received your offer"
    },
    "successEmailHeader": { //<-- OPTIONAL - Success email header
      "el": "Έχουμε λάβει την προσφορά σας",
      "en": "We have received your offer",
      "tr": "We have received your offer"
    },
    "description": {  //<-- Service description (meta)
      "el": "[Υποβάλετε αίτηση για ...]",
      "en": "[Submit an application ...]",
      "tr": ""
    },
    "url": "https://gov.cy", //<-- URL in (meta, for example `og:url`)
    "cdn": {                 //<-- CDN URL and integrity
      "dist": "https://cdn.jsdelivr.net/gh/gov-cy/[email protected]/dist",
      "cssIntegrity": "sha384-qjx16YXHG+Vq/NVtwU2aDTc7DoLOyaVNuOHrwA3aTrckpM/ycxZoR5dx7ezNJ/Lv",
      "jsIntegrity": "sha384-tqEyCdi3GS4uDXctplAd7ODjiK5fo2Xlqv65e8w/cVvrcBf89tsxXFHXXNiUDyM7"
    },
    "submissionDataVersion": "1",     //<-- Submission data version
    "rendererVersion": "1.16.1",       //<-- govcy-frontend-renderer version
    "designSystemsVersion": "3.2.0",  //<-- govcy-design-system version
    "homeRedirectPage": {               //<-- Home redirect page
      "el": "https://www.gov.cy/service/aitisi-gia-taftotita/",
      "en": "https://www.gov.cy/en/service/issue-an-id-card/",
      "tr": "https://www.gov.cy/en/service/issue-an-id-card/"
    },
    "copyrightText": {                  //<-- Copyright text
      "el": "Κυπριακή Δημοκρατία, 2025",
      "en": "Republic of Cyprus, 2025",
      "tr": "Republic of Cyprus, 2025"
    },
    "submissionAPIEndpoint": {          //<-- Submission API endpoint
      "url": "TEST_SUBMISSION_API_URL",
      "method": "POST",
      "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
      "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
      "dsfgtwApiKey": "TEST_SUBMISSION_DSF_GTW_KEY",
      "response": {
        "errorResponse": {
          "102": {
            "error": "user not administrator",
            "page": "/test/user-not-admin"
          },
          "105": {
            "error": "user not registration",
            "page": "/test/user-not-registered"
          }
        }
      }
    },
    "submissionGetAPIEndpoint": {     //<-- Submission GET API endpoint for temporary saving
      "url": "TEST_SUBMISSION_GET_API_URL",
      "method": "GET",
      "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
      "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
      "dsfgtwApiKey": "TEST_SUBMISSION_DSF_GTW_KEY"
    },
    "submissionPutAPIEndpoint": {    //<-- Submission PUT API endpoint for temporary saving
      "url": "TEST_SUBMISSION_PUT_API_URL",
      "method": "PUT",
      "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
      "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
      "dsfgtwApiKey": "TEST_SUBMISSION_DSF_GTW_KEY"
    },
    "fileUploadAPIEndpoint": {       //<-- File upload API endpoint
      "url": "TEST_UPLOAD_FILE_API_URL",
      "method": "POST",
      "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
      "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
      "dsfgtwApiKey": "TEST_SUBMISSION_DSF_GTW_KEY"
    },
    "fileDownloadAPIEndpoint": {     //<-- File download API endpoint
      "url": "TEST_DOWNLOAD_FILE_API_URL",
      "method": "GET",
      "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
      "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
      "dsfgtwApiKey": "TEST_SUBMISSION_DSF_GTW_KEY"
    },
    "fileDeleteAPIEndpoint": {     //<-- File delete API endpoint
      "url": "TEST_DELETE_FILE_API_URL",
      "method": "DELETE",
      "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
      "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
      "dsfgtwApiKey": "TEST_SUBMISSION_DSF_GTW_KEY"
    },
    "eligibilityAPIEndpoints": [     //<-- Eligibility API endpoints
      {
        "url": "TEST_ELIGIBILITY_2_API_URL",
        "method": "GET",
        "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
        "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
        "dsfgtwApiKey": "TEST_SUBMISSION_DSF_GTW_KEY",
        "cashingTimeoutMinutes": "60",
        "params": {},
        "response": {
          "errorResponse": {
            "105": {
              "error": "user not registration",
              "page": "/test/user-not-registered"
            }
          }
        }
      }
    ]
  },
  "pages": [                 //<-- Pages
    {
      "pageData": {            //<-- 1st Page's data (form)
        "url": "index",        // Page URL
        "title": {             // Page title
          "el": "Επιλογή Εγγάφου",
          "en": "Document selection",
          "tr": ""
        },
        "layout": "layouts/govcyBase.njk",  // Page layout
        "mainLayout": "two-third",          // Page main layout
        "nextPage": "data-entry-radios"     // The next page's URL
      },
      "pageTemplate": {         //<-- Page template
        "sections": [           //<-- Page sections
          {
            "name": "main",   //<-- Main section
            "elements": [     //<-- Main section elements
              {
                "element": "form",  // Form element
                "params": {
                  "elements": [     // Elements inside the form
                    {
                      "element": "checkboxes",  // Checkboxes element
                      "params": {               // Checkboxes parameters
                        "id": "certificate_select",
                        "name": "certificate_select",
                        "legend": {
                          "el": "Τι έγγραφα επιθυμείτε να εκδώσετε;",
                          "en": "What documents do you wish to issue?"
                        },
                        "items": [
                          {
                            "value": "birth",
                            "text": {
                              "el": "Πιστοποιητικό γέννησης​",
                              "en": "Birth certificate",
                              "tr": ""
                            },
                            "hint": {
                              "el": "Αν η γέννηση έγινε στην Κύπρο ή στο εξωτερικό και έχει ενημερωθεί το μητρώο του Αρχείου Πληθυσμού ",
                              "en": "For a birth in Cyprus or abroad which Civil Registry is updated with "
                            }
                          },
                          {
                            "value": "permanent_residence",
                            "text": {
                              "el": "Βεβαίωση μόνιμης διαμονής​",
                              "en": "Certificate of permanent residence",
                              "tr": ""
                            },
                            "hint": {
                              "el": "Για όσους είναι εγγεγραμμένοι στον εκλογικό κατάλογο",
                              "en": "For those registered in the electoral list"
                            }
                          },
                          {
                            "value": "student_proof_of_origin",
                            "text": {
                              "el": "Βεβαίωση καταγωγής",
                              "en": "Certificate of origin",
                              "tr": ""
                            },
                            "hint": {
                              "el": "Για αίτηση σε πανεπιστήμια στην Ελλάδα",
                              "en": "To apply to a university in Greece"
                            }
                          }
                        ],
                        "isPageHeading": true,
                        "hint": {
                          "el": "Επιλέξτε ένα ή περισσότερα έγγραφα",
                          "en": "Select one or more documents",
                          "tr": ""
                        }
                      },
                      "validations": [      // Checkboxes validations
                        {
                          "check": "required",
                          "params": {
                            "checkValue": "",
                            "message": {
                              "el": "Επιλέξετε ένα ή περισσότερα έγγραφα",
                              "en": "Select one or more documents",
                              "tr": ""
                            }
                          }
                        }
                      ]
                    },
                    {
                      "element": "button",
                      "params": {
                        "id": "continue",
                        "variant": "primary",
                        "text": {
                          "el": "Συνέχεια",
                          "en": "Continue"
                        }
                      }
                    }
                  ]
                }
              }
            ]
          }
        ]
      }
    },
    {
      "pageData": {             //<-- 2nd Page's data (form)
        "url": "data-entry-radios",
        "title": {
          "el": "Στοιχεία επικοινωνίας ",
          "en": "Contact details",
          "tr": ""
        },
        "layout": "layouts/govcyBase.njk",
        "mainLayout": "two-third",
        "nextPage": "review"
      },
      "pageTemplate": {
        "sections": [
          {
            "name": "beforeMain",
            "elements": [
              {
                "element": "backLink",
                "params": {}
              }
            ]
          },
          {
            "name": "main",
            "elements": [
              {
                "element": "form",
                "params": {
                  "elements": [
                    {
                      "element": "radios",
                      "params": {
                        "id": "mobile_select",
                        "name": "mobile_select",
                        "legend": {
                          "el": "Σε ποιο κινητό μπορούμε να επικοινωνήσουμε μαζί σας;",
                          "en": "What mobile number can we use to contact you?"
                        },
                        "items": [
                          {
                            "value": "mobile",
                            "text": {
                              "el": "Στο [99 123456]",
                              "en": "You can use [99 123456]",
                              "tr": ""
                            }
                          },
                          {
                            "value": "other",
                            "text": {
                              "el": "Θα δώσω άλλο αριθμό",
                              "en": "I will give a different number",
                              "tr": ""
                            },
                            "conditionalElements": [
                              {
                                "element": "fileInput",
                                "params": {
                                  "id": "proof",
                                  "name": "proof",
                                  "label": {
                                    "el": "Αποδεικτικό τηλεφώνου",
                                    "en": "Telephone proof",
                                    "tr": ""
                                  },
                                  "isPageHeading": false,
                                  "hint": {
                                    "el": "PDF, JPG, JPEG, PNG, είναι οι αποδεκτές μορφές",
                                    "en": "PDF, JPG, JPEG, PNG are the acceptable formats",
                                    "tr": ""
                                  }
                                },
                                "validations": [
                                  {
                                    "check": "required",
                                    "params": {
                                      "checkValue": "",
                                      "message": {
                                        "el": "Ανεβάστε τον αποδεικτικό τηλεφώνου",
                                        "en": "Upload the telephone proof",
                                        "tr": ""
                                      }
                                    }
                                  }
                                ]
                              }
                            ]
                          }
                        ],
                        "isPageHeading": true
                      },
                      "validations": [
                        {
                          "check": "required",
                          "params": {
                            "checkValue": "",
                            "message": {
                              "el": "Επιλέξετε αν θέλετε να χρησιμοποιήσετε το τηλέφωνο που φαίνεται εδώ, ή κάποιο άλλο",
                              "en": "Choose if you'd like to use the phone number shown here, or a different one",
                              "tr": ""
                            }
                          }
                        }
                      ]
                    },
                    {
                      "element": "button",
                      "params": {
                        "id": "continue",
                        "variant": "primary",
                        "text": {
                          "el": "Συνέχεια",
                          "en": "Continue"
                        }
                      }
                    }
                  ]
                }
              }
            ]
          }
        ]
      }
    },
    {
      "pageData": {         //<-- 3rd Page's data (not a form)
        "url": "user-not-registered",
        "title": {
          "el": "Δεν είστε εγγεγραμμένοι ",
          "en": "You are not an registered",
          "tr": ""
        },
        "layout": "layouts/govcyBase.njk",
        "mainLayout": "two-third"
      },
      "pageTemplate": {
        "sections": [
          {
            "name": "beforeMain",
            "elements": []
          },
          {
            "name": "main",
            "elements": [
              {
                "element": "textElement",
                "params": {
                  "id": "title",
                  "type": "h1",
                  "text": {
                    "el": "Δεν είστε εγγεγραμμένοι",
                    "en": "You are not registered"
                  }
                }
              },
              {
                "element": "htmlElement",
                "params": {
                  "id": "body",
                  "text": {
                    "el": "<p>Για να υποβάλετε σε υπηρεσία αυτή, χρειάζεται να είστε εγγεγραμμένοι στο ΧΥΖ.</p>",
                    "en": "<p>To submit in this service you need to be registered at XYZ.</p>"
                  }
                }
              }
            ]
          }
        ]
      }
    },
    {
      "pageData": {         //<-- 4th Page's data (not a form)
        "url": "user-not-admin",
        "title": {
          "el": "Δεν είστε διαχειριστής ",
          "en": "You are not an administrator",
          "tr": ""
        },
        "layout": "layouts/govcyBase.njk",
        "mainLayout": "two-third"
      },
      "pageTemplate": {
        "sections": [
          {
            "name": "beforeMain",
            "elements": []
          },
          {
            "name": "main",
            "elements": [
              {
                "element": "textElement",
                "params": {
                  "id": "title",
                  "type": "h1",
                  "text": {
                    "el": "Δεν είστε διαχειριστής ",
                    "en": "You are not an administrator"
                  }
                }
              },
              {
                "element": "htmlElement",
                "params": {
                  "id": "body",
                  "text": {
                    "el": "<p>Για να υποβάλετε σε υπηρεσία αυτή, χρειάζεται να είστε διαχειριστής στο ΧΥΖ.</p>",
                    "en": "<p>To submit in this service you need to be an administrator of XYZ.</p>"
                  }
                }
              }
            ]
          }
        ]
      }
    }
  ]
}

Here are some details explaining the JSON structure:

  • site object: Contains information about the site, including the site ID, language, and footer links. See govcy-frontend-renderer for more details. Some fields that are only specific to the govcy-express-forms project are the following:
    • usesDSFSubmissionPlatform: A boolean that indicates whether the service uses the DSF submission platform (transforms submission data as needed)
    • cyLoginPolicies: which types of authenticated CY Login profiles are allowed to access the service
    • submissionDataVersion : The submission data version,
    • rendererVersion : The govcy-frontend-renderer version,
    • designSystemsVersion : The govcy-design-system version,
    • homeRedirectPage: An object mapping language codes to URLs. When a user visits the root route (e.g., https://whatever-your-service-is.service.gov.cy/), the system redirects to the URL for the user's language. If the user's language is not found, it falls back to "el" or the first available URL. If not provided, a list of available sites is shown. Example:
    "homeRedirectPage": {
      "el": "https://www.gov.cy/service/aitisi-gia-taftotita/",
      "en": "https://www.gov.cy/en/service/issue-an-id-card/"
    }
    • eligibilityAPIEndpoints : An array of API endpoints, to be used for service eligibility. See more on the Eligibility API Endoints section below.
    • submissionAPIEndpoint: The submission API endpoint, to be used for submitting the form. See more on the Submission API Endoint section below.
    • submissionGetAPIEndpoint: The submission get API endpoint, to be used for getting the submission data. See more on the temporary save feature section below.
    • submissionPutAPIEndpoint: The submission put API endpoint, to be used for temporary saving the submission data. See more on the temporary save feature section below.
    • fileUploadAPIEndpoint: The file upload API endpoint, to be used for uploading files. See more on the file upload feature section below.
    • fileDownloadAPIEndpoint: The file download API endpoint, to be used for downloading files. See more on the file upload feature section below.
    • fileDeleteAPIEndpoint: The file delete API endpoint, to be used for deleting files. See more on the file upload feature section below.
  • pages array: An array of page objects, each representing a page in the site.
    • pageData object: Contains the metadata to be rendered on the page. See govcy-frontend-renderer for more details
      • nextPage: The URL of the next page to be rendered after the user clicks the continue button.
    • pageTemplate object: Contains the page template to be rendered on the page. See govcy-frontend-renderer for more details

A typical service flow that includes pages index, question-1, question-2 under the pages array in the JSON file looks like this:

flowchart LR
    govcy-page --> isAuth{Is User Authenticated?}
    isAuth -- Yes<br><br> Eligibility Check --> index([:siteId/index])
    isAuth -- No --> cyLogin[cyLogin]
    cyLogin -- Eligibility Check --> index
    index --  Eligibility Check<br> Validations<br> Conditionals --> question-1[:siteId/question-1]
    question-1 --  Eligibility Check<br> Validations<br> Conditionals --> question-2[:siteId/question-2]
    question-2 --  Eligibility Check<br> Validations<br> Conditionals --> review[📄:siteId/review <br> <br> Automatically generated]
    review --  Eligibility Check<br> All Validations<br> All Conditionals --> success([✅:siteId/success <br> <br> Automatically generated])

Some pages are generated automatically by the project, such as the review and success pages.


Pages

Pages defined in the JSON file under the pages array, they rendered based on the govcy-frontend-renderer library, and they are served by the /:siteId/:pageUrl route. The pageData.nextPage field is used to determine the next page to render.

Here's an example of a page defined in the JSON file:

{
  "pageData": {
    "url": "index",
    "title": {
      "el": "Your email",
      "en": "Το email σας"
    },
    "layout": "layouts/govcyBase.njk",
    "mainLayout": "two-third",
    "nextPage": "telephone-number",
    "conditions": [
      {
        "expression": "dataLayer['test-service.inputData.somePage.formData.showExtra'] != 'yes'",
        "redirect": "review"
      }
    ]
  },
  "pageTemplate": {
    "sections": [
      {
        "name": "beforeMain",
        "elements": [
          {
            "element": "backLink",
            "params": {}
          }
        ]
      },
      {
        "name": "main",
        "elements": [
          {
            "element": "form",   
            "params": {
              "elements": [
                {
                  "element": "textInput",
                  "params": {
                      "label": {
                          "en": "What is your email?",
                          "el": "Ποιο είναι το email σας?"
                      },
                      "id": "email",
                      "name": "email",
                      "hint": {
                          "en": "We’ll only use this email for this application",
                          "el": "Θα χρησιμοποιήσουμε το email σας μόνο για αυτήν την υπηρεσία"
                      },
                      "type": "email",
                      "isPageHeading": true,
                      "fixedWidth": "50"
                  },
                  "validations": [
                    {
                        "check": "required",
                        "params": {
                        "message": {
                            "en": "Enter your email",
                            "el": "Εισαγάγετε το email σας"
                        }
                        }
                    },
                    {
                        "check": "valid",
                        "params": {
                        "checkValue": "email",
                        "message": {
                            "en": "Your email must be a valid email address",
                            "el": "To emial πρέπει να είναι έχει μορφή email address"
                        }
                        }
                    }
                ]
                },
                {
                  "element": "button",
                  "params": {
                    "id": "continue",
                    "variant": "primary",
                    "text": {
                      "el": "Συνέχεια",
                      "en": "Continue"
                    }
                  }
                }
              ]
            }
          }
        ]
      }
    ]
  }
}

The above page JSON generates a page that looks like the following screenshot:

Screenshot of sample page

The JSON structure is based on the govcy-frontend-renderer's JSON template.

Lets break down the JSON config for this page:

  • pageData are the page's meta data, such as the URL, title, layout, mainLayout, and nextPage.
    • pageData.url is the URL of the page, in this case it's :siteId/index
    • pageData.title is the title of the page, in this case it's Your email. This will be used in the review, success pages, the PDF, the email, and the submission platform.
    • pageData.layout is the layout used to render the page. The project only supports the default layout layouts/govcyBase.njk
    • pageData.mainLayout is the layout of the main section of the page, in this case it's two-third. It can be either two-third or max-width,
    • pageData.nextPage is the next page to redirect to when the user clicks the continue button and all validations pass, in this case it will redirect to /:siteId/telephone-number
    • pageData.conditions is the array that defines the conditional logic
  • pageTemplate is the page's template, which is a JSON object that contains the sections and elements of the page. Check out the govcy-frontend-renderer's documentation for more details.

Form vs static pages

  • If the pageTemplate includes a form element in the main section and button element, the system will treat it as form and will:
    • Perform the eligibility checks
    • Display the form
    • Collect the form data for the following input elements (more details on the govcy-frontend-renderer's design elements documentation):
      • textInput
      • textArea
      • select
      • radios
      • checkboxes
      • datePicker
      • dateInput
      • fileInput: the file upload feature must be enabled to use this element (see more on the file upload feature section below)
    • Validate the form data (see more on the Input validations section below)
    • Store the form data in the systems data layer
    • Redirect the user to the next page (or review page if the user came from the review page)
  • Else if the pageTemplate does not include a form element in the main section, the system will treat it as static content and will:
    • Not perform the eligibility checks
    • Display the static content

When designing form pages, refer to the Unified Design System's question pages pattern.

Error pages Pages that can be used to display messages when eligibility or submission fail are simply static content pages. That is pages that do not include a form element.

Start page The start page should be created in the gov.cy portal and should be defined in the site.homeRedirectPage property in the site config JSON file. All pages within a service are private by default and can only be accessed by authenticated users, so the start page cannot be created in the JSON file.

Notes:


Update my details pages

Update my details seamless

Update my details pages are pages that can integrate the Update My Personal Details service to fetch or update a user’s name, email, mobile number, or correspondence address, without breaking the user journey. The implementation of these pages follow the instructuctions described in the Use ‘Update my personal details’ in Your Service post.

If the scope also includes the email element, the system will also use that email address to send the email to the user on submition.

Update my details - example JSON config

{
  "pageData": {
    "url": "index",     // Page URL
    "layout": "layouts/govcyBase.njk",
    "mainLayout": "two-third",
    "nextPage": "next-page" 
  },
  "updateMyDetails": {
    "APIEndpoint": {    // API endpoint for fetching user details from the Update My Details service
      "url": "CIVIL_REGISTRY_CONTACT_API_URL", // URL
      "method": "GET",                         // HTTP method
      "clientKey": "DSF_API_GTW_CLIENT_ID",    // Client key
      "serviceId": "DSF_API_GTW_SERVICE_ID",   // Service ID
      "dsfgtwApiKey": "DSF_API_GTW_SECRET"     // DSF GTW API key 
    },
    "updateMyDetailsURL": "UPDATE_MY_DETAILS_URL",  // DOMAIN URL for redirecting to the Update My Details service
  "topElements": [                             // Elements to be displayed on the top of the page
      {
        "element": "progressList",
        "params": {
          "id": "steps",
          "current": "4",
          "total": "4",
          "showSteps": true
        }
      }
    ],
    "scope": [                                  // Scope of the form. What elenents to collect
      "fullName",
      "email",
      "mobile",
      "address"
    ],
    "hasBackLink": true                         // Whether the hub page has a back link
  }
}

Lets break down the JSON config for Update my details:

  • updateMyDetails are the page's definition for the integration with the Update My Details service.
    • updateMyDetails.APIEndpoint is the API endpoint for fetching user details from the Update My Details service.
    • updateMyDetails.updateMyDetailsURL is the DOMAIN URL for redirecting to the Update My Details service.
    • updateMyDetails.scope is the scope of the form. What elenents to collect. It can be one or more of the following:
      • fullName
      • dob
      • email
      • mobile
      • address
    • updateMyDetails.hasBackLink is a boolean that indicates whether the hub page has a back link.

The above config references the following environment variables that need to be set:

### Update my details
CIVIL_REGISTRY_CONTACT_API_URL=http://localhost:3002/get-update-my-details
DSF_API_GTW_CLIENT_ID=your-DSF-API-gateway-client-id
DSF_API_GTW_SERVICE_ID=your-DSF-API-gateway-service-id
DSF_API_GTW_SECRET=your-DSF-API-gateway-secret

UPDATE_MY_DETAILS_URL=https://update-my-details.staging.service.gov.cy # FOR TESTING

The DSF team has developed an API that performs standard eligibility checks against the Civil Registry. More details at Update-my-details.md

Update my details - users' flow

The update my details behaves like a normal page and it is accessed through the url :siteId/:pageUrl. As a best practice you should set this as your index page and the first one in your pages array. Depending on the user it can have 3 variants:

Variant 1: Manual form for non-eligible users (no access to UMD)

When a user is either:

  • Not a Cypriot citizen
  • Or Cypriot citizen under 18

The user gets a data entry page.

variant 1 screenshot

Variant 2: Eligible users (access to UMD) with existing details

When a user is :

  • A Cypriot citizen over 18
  • AND has data in Update my Details

The users get a page with the data from UMD and asks if its ok to use those data.

variant 2 screenshot

If the user selects:

  • YES: the data are stored in the data layer and continues to the next page (or review page depending where the user came from)
  • NO: the users are redirected to the Update my details service in seamless mode. When users finish with that, they are redirected back to the page with variant 2 and are asked again if it's ok to use the updated data.

Variant 3: Eligible users (access to UMD) without existing details

When a user is :

  • A Cypriot citizen over 18
  • AND has no data in Update my Details

The users get a continue button that redirects to the Update my details service in seamless mode.

variant 3 screenshot

When users finish with that, they are redirected back to the page with variant 2 and are asked if it's ok to use the updated data.

Update my details - data storage

The form data for a updateMyDetails page is stored as a normal page as an object in the session data layer:

{
  "index": {
    "formData": 
    {
      "fullName": "John Smith",
      "email": "[email protected]",
      "mobile": "+35712345678",
      "address": "123 Some Street\n Nicosia\nCyprus",
    }
  }
}

Notes on Update my details

  • There pageData.title is not used. Instead the page title is generated by the system.

Multiple things pages (repeating group of inputs)

Multiple things pages

Some services need to collect multiple entries of the same structure, for example, academic qualifications, addresses, or dependents. The framework supports this through the multipleThings block in a page config. It uses the same input method of a normal form page, but the user can add more than one entries.

When enabled, the framework automatically generates a hub page when the user visits the:

/:siteId/:pageUrl

This hub page allows users to:

  • 📋 View the list of entries (hub page)
  • Add a new entry
  • ✏️ Change an existing entry
  • Remove an existing entry
  • Continue when they are finished

Multiple things - example JSON config Here’s how to define a page that collects multiple academic qualifications:

{
  "pageData": {
    "url": "qualifications",    // Page URL
    "title": {                  // Page title of the input pages (add, edit)
      "el": "Ακαδημαϊκά και επαγγελματικά προσόντα",
      "en": "Academic and professional qualifications",
      "tr": ""
    },
    "layout": "layouts/govcyBase.njk",  // Page layout for all pages (add, edit, hub page)
    "mainLayout": "two-third",          // Page main layout for all pages (add, edit, hub page)
    "nextPage": "memberships"           // The next page's URL
  },
  "pageTemplate" : {  // Page template for the input pages (add, edit)
    ...
  },
  "multipleThings": { // Multiple things configuration
    "min": 1,     // the minimum number of entries
    "max": 5,     // the maximum number of entries
    "dedupe": true, // whether to check for duplicates
    "itemTitleTemplate": "{{title | trim}}  - {{year | trim}}", // the title template for each entry
    "listPage": { // the hub page
      "title": { // the hub page title
        "el": "Ακαδημαϊκά προσόντα",
        "en": "Academic details"
      },
      "topElements": [ // the hub page top elements
        {
          "element": "progressList",
          "params": {
            "id": "progress",
            "current": "2",
            "total": "6",
            "showSteps": true
          }
        },
        {
          "element": "textElement",
          "params": {
            "id": "header",
            "type": "h1",
            "text": {
              "el": "Ποια είναι τα προσόντα σας;",
              "en": "What are your qualifications?",
              "tr": ""
            }
          }
        },
        {
          "element": "textElement",
          "params": {
            "id": "instructions",
            "type": "p",
            "text": {
              "el": "Προσθέστε τουλάχιστον ένα τα ακαδημαϊκό ή επαγγελματικό προσόν για να συνεχίσετε. Μπορείτε να προσθέσετε μέχρι 5.",
              "en": "Add at least one academic or professional qualifications to proceed. You can up to 5."
            }
          }
        }
      ],
      "emptyState": { // the hub page empty state
        "en": "No qualifications added yet.",
        "el": "Δεν έχετε προσθέσει ακόμη προσόντα."
      },
      "addButtonText": { // the hub page add button text
        "en": "➕ Add qualifications",
        "el": "➕ Προσθήκη προσόντος"
      },
      "addButtonPlacement": "top", // the hub page add button placement
      "continueButtonText": { // the hub page continue button text
        "en": "Save and continue",
        "el": "Αποθήκευση και συνέχεια"
      },
      "hasBackLink": true // whether the hub page has a back link
    }
  }
}

Lets break down the JSON config for multiple things:

  • multipleThings are the page's definition for repeated group of inputs
    • multipleThings.min : The minimum items rule
    • multipleThings.max : The maximum items rule
    • multipleThings.dedupe: When true prevents duplicates, using the itemTitleTemplate template. Optional with default value false
    • multipleThings.itemTitleTemplate: The template (in Nunjucks form) used to display the items' on a list in the hub, review, success pages and email. Also used by the dedupe to compare for duplicates
    • multipleThings.listPage: The definition the list (hub) page that shows the list of items, with the actions (add, edit, delete, continue)
      • multipleThings.listPage.title: The title used in the hub page's meta data
      • multipleThings.listPage.topElements: The elements to be displayed on the top of the page (more details on the govcy-frontend-renderer's design elements documentation):
      • multipleThings.listPage.emptyState: The message displayed when the count of items == 0. Optional, when not defined generic text is shown
      • multipleThings.listPage.addButtonText: The text displayed on the add link. Optional, when not defined generic text is shown
      • multipleThings.listPage.addButtonPlacement: Where to show the add link. Can be top, bottom or both. Optional, default is bottom
      • multipleThings.listPage.continueButtonText: The text displayed on the continue button text. Optional, default is Continue, Συνέχεια
      • multipleThings.listPage.hasBackLink: When true shows the standard back button. Optional, default is `true

Multiple things - How it works With multiple things pages, the system collects multiple set of the same type of data. The set of data are collected by a single page (similar to a normal page). The multiple things consist of 4 type of pages, the hub, add, edit and delete pages

Hub page: When the user navigates through the service at /:siteId/:pageUrl (either coming from review or linear flow), the system shows the hub page. The hub page in general shows the list of entries, add links, continue button and validates the input (see below more details)

Hub page add links: If the user has not reached the maximum number of allowed entries, the system shows add links on the hub page. There is an option for a custom add link text with multipleThings.listPage.addButtonText. There is an option for top, bottom or both placement with multipleThings.listPage.addButtonPlacement

Multiple things hub - add links

Hub empty state: When no data are yet entered the hub shows an empty state message with an add link. There is an option for a custom empty state

Multiple things hub - empty state

Hub list state: When data exist, they are shown as a list with change and delete links. The list is created based on an the multipleThings.itemTitleTemplate, for example {{institution}} - {{title}} - {{year}}

Multiple things hub - list

Hub max state: If the data entries reached the maximum limit, the add links are removed and a max limit reached message is shown.

Multiple things hub - max

Hub page continue: on Continue the system continues to the next page defined in the pageData.nextPage. If user came from the review page, it returns to the review.

Hub page validations: The hub page validates the following when the continue button is pressed.

  • Minimum entries have been entered. This is based on the multipleThings.min.
  • Maximum limit has not been exceeded.. This is based on the multipleThings.max.
  • All entries validations pass. This is based on the pageTemplate element validations.

Multiple things hub - validations

Add pages

Multiple things add

  • Accessible through the add link or through the /:siteId/:pageUrl/multiple/add route
  • User is shown the item page (e.g. academic-details)
  • Files' data are stored in a draft until they click Continue
  • On Continue, the data and draft is pushed into the list
  • Behaves like a normal form page
  • On Continue if there are no validation errors, the user is navigated back to the hub and the added data is displayed on the list. If the user’s journey started from the review page, after clicking continue on the hub, it goes back to the review.
  • It performs all configured input validations defined PLUS
    • Maximum limit has not been exceeded.
    • Dedupe validation. If defined in the configuration the service checks if there is an identical entry. The check is made based on the item title template

Edit pages

Multiple things edit

  • Accessible through the change link or through the /:siteId/:pageUrl/multiple/edit/:index route
  • User selects an existing item from the hub page
  • The item page loads pre-filled data
  • Behaves like a normal form page
  • On Continue if there are no validation errors, the entry is updated and the user is navigated back to the hub and the updated data is displayed on the list. If the user’s journey started from the review page, after clicking continue on the hub, it goes back to the review.
  • It performs all configured input validations defined PLUS
  • Dedupe validation. If defined in the configuration the service checks if there is an identical entry. The check is made based on the item title template

Delete pages

Multiple things delete

  • Accessible through the remove link or through the /:siteId/:pageUrl/multiple/delete/:index route
  • A confirmation page is shown
  • On Yes, the item is removed from the list

Review & Success pages

Multiple things review

  • Each item is displayed in the summary list, grouped under the hub’s title

Multiple things - data storage

Form data for a multipleThings page is stored as an array in the session data layer:

{
  "academic-details": {
    "formData": [
      {
        "title": "BSc Computer Science",
        "academicFile": {
          "fileId": "12345",
          "sha256": "abcdef..."
        }
      },
      {
        "title": "MSc Information Systems",
        "academicFile": {
          "fileId": "67890",
          "sha256": "ghijkl..."
        }
      }
    ]
  }
}

Notes on multiple things

  • Each multipleThings page must define its own item page (with fields and validations), used for the add add edit routes.
  • The hub page is generated automatically. It uses the multipleThings.listPage for the UI
  • Empty state is handled automatically.
  • multipleDraft is used internally to store file's data while the user is adding a new item.

Review page

The review page is automatically generated by the project and includes the following sections:

  • Summary: A summary of the data from all the pages in the service.
  • Change links: A list of links to each page in the service.
  • Submit button: A button to submit the form.

Here's an example screenshot of review page

Screenshot of review page

When the user clicks a change link, the user is redirected to the corresponding page in the service. After the user clicks on continue button the user is redirected back to the review page.

When the user clicks the Submit button, all the data gathered from the site's forms within this session are validated based on the validation definition in the JSON file, and if they pass they are submitted to the configured API endpoint.


Success page

The success page is automatically generated by the project, is accessible only when a submission is made successfully, and includes the following sections:

  • Success banner: A banner indicating that the form was successfully submitted, with the reference number of the submission.
  • PDF Download link: A link to download the PDF of the submission's data in a human-readable format.
  • Summary: A summary of the data from all the pages in the service.

Here's an example screenshot of success page

Screenshot of success page


🛡️ Site eligibility checks

The project uses an array of API endpoints to check the eligibility of a service/site. To use this feature, you need to configure the following in your JSON file under the site object:

"eligibilityAPIEndpoints" : [
  {
    "url": "TEST_ELIGIBILITY_1_API_URL",
    "method": "POST", 
    "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
    "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
    "cashingTimeoutMinutes": 2,
    "params": {
      "checkFor": "isCitizen,isAdult"
    },
    "response": {
      "errorResponse": {
        "102": {
          "error": "user not administrator",
          "page": "/test/user-not-admin"
        }
      }
    }
  },
  {
    "url": "TEST_ELIGIBILITY_2_API_URL",
    "clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
    "serviceId": "TEST_SUBMISSION_API_SERVIVE_ID",
    "cashingTimeoutMinutes": 60,
    "response": {
      "errorResponse": {
        "105": {
          "error": "user not registration",
          "page": "/test/user-not-registered"
        }
      }
    }
  }
]

If no eligibilityAPIEndpoints are configured, the system will not check for service eligibility for the specific site.

Lets break the JSON config down:

  • eligibilityAPIEndpoints : An array of API endpoints, to be used for service eligibility.
    • url: The enviromental variable that holds the URL of the API endpoint.
    • method: The HTTP method to use when making the request.
    • clientId: The enviromental variable that holds the client ID to use when making the request.
    • clientSecret: The enviromental variable that holds the client secret to use when making the request.
    • dsfgtwApiKey (optional): To be used only when using APIs through the DSF gateway instead of cyConnect
    • cashingTimeoutMinutes: The number of minutes to cache the response from the API endpoint. If set to 0, the API endpoint will be called every time.
    • params: An object of key-value pairs that will be added to the request body when making the request.
    • response: An object of expected response when succeeded===false, to be used for the system to know which error page to show.

The above config references the following environment variables that need to be set:

TEST_ELIGIBILITY_1_API_URL=http://localhost:3002/check1
TEST_ELIGIBILITY_2_API_URL=http://localhost:3002/check2
TEST_SUBMISSION_API_CLIENT_KEY=12345678901234567890123456789000
TEST_SUBMISSION_API_SERVIVE_ID=123

With the above config, when a user visits a page under the specific site, /:siteId/*, the service sends a request to the configured eligibility API endpoints. If any of the API endpoints returns succeeded: false, the user is redirected to the error page specified in the response object.

The response is cached to the session storage for the specified number of minutes. If the cashingTimeoutMinutes is set to 0, the API endpoint will be called every time.

Here's a flowchart showing how the eligibility checks work:

flowchart LR
    A[🧭 User visits /:siteId/* page] --> B{{❓ Are eligibilityAPIEndpoints configured?}}
    B -- No --> H[✅ Access granted<br>Show page]
    B -- Yes --> D[🔁 Loop through API endpoints]

    D --> D1{{❓ Is cached response still valid?}}
    D1 -- Yes --> D2[🗃️ Use cached result]
    D1 -- No --> E[🔄 Send request with:<br>- Method GET or POST<br>- Auth header<br>- Params or body]

    D2 --> F{{❓ Did cached result<br>have Succeeded: true?}}
    E --> F

    F -- Yes --> G{{❓ More endpoints to check?}}
    G -- Yes --> D
    G -- No --> H

    F -- No --> I[📄 Check ErrorCode<br>in config]
    I --> J{{❓ Is ErrorCode in config?}}
    J -- Yes --> K[❌ Redirect to configured error page]
    J -- No --> L[❌ Show generic error page]

Eligibility API request and response

For each eligibility API endpoint, the project sends a request to the API endpoint. The project uses the CY Connect - OAuth 2.0 (CY Login) authentication policy, so the user's <access_token> is sent in the Authorization header.

Eligibility Request

  • HTTP Method:
    • Defined per endpoint in the method property (defaults to GET if not specified).
  • URL:
    • Resolved from the url property in your config (from the environment variable).
  • *Headers