What you will learn
Build a fictional service-enquiry form that prepares an email for the project owner. Start with a free local preview, then study an optional paid-send path with clear consent and duplicate-click handling. The deliverable is a reviewed notification flow with honest status messages, not a bulk email system or a customer mailing list.
Understand the fixed recipient and the cost first
Gammal Tech’s notification contract exposes GammalTech.notify(subject, html). It always targets the project owner’s verified email. There is no recipient argument for a visitor’s address, no arbitrary to, cc or from, and no login requirement for the visitor. Requests still use the registered-domain model. The service retains a monitored copy; include that destination in your client’s data review.
As published on 12 October 2026, each delivered email costs 0.40 EGP with no free allowance. Funding the wallet is separate from assigning balance to the project. Complete the local preview without paying. An actual send is optional and requires the owner’s approval and available project funds. For client work, the client controls their own project and account; never request shared login credentials.
Create a small form using fictional content
Use two approved enquiry types and a short fictional message. Do not collect an identity document, session token, payment information or sensitive customer record.
<label for="service">Fictional enquiry type</label>
<select id="service">
<option value="website-review">Website review</option>
<option value="training">Training</option>
</select>
<label for="message">Fictional message (10-500 characters)</label>
<textarea id="message" maxlength="500"></textarea>
<button id="preview" type="button">Preview without sending</button>
<pre id="notification-preview"></pre>
<label><input id="cost-consent" type="checkbox">
I approve one paid owner notification.
</label>
<button id="send" type="button">Optional paid send</button>
<p id="notification-status" role="status"></p>Place the supporting JavaScript after these elements in your own exercise file. Explain the price beside the optional send controls in the real page. The checkbox records a deliberate local action; it is not a trusted authorization system. For a preview-only submission, omit the optional send handler and do not load the SDK.
Validate the choices and escape the visitor text
Use a fixed subject and a small allowlist of enquiry types. The following builder escapes visitor text before placing it inside a fixed HTML paragraph. OWASP’s output-handling guidance explains why the intended output context matters.
const service = document.getElementById("service");
const message = document.getElementById("message");
const status = document.getElementById("notification-status");
const preview = document.getElementById("notification-preview");
const escapeHtml = value => value.replace(/[&<>"']/g, c => ({
"&": "&", "<": "<", ">": ">",
'"': """, "'": "'"
}[c]));
function buildNotification() {
const choices = { "website-review": "Website review", training: "Training" };
if (!Object.hasOwn(choices, service.value)) throw new Error();
const text = message.value.trim();
if (text.length < 10 || text.length > 500) throw new Error();
return {
subject: "Learning lab: fictional service enquiry",
html: "<p>Type: " + choices[service.value] + "</p><p>"
+ escapeHtml(text) + "</p>"
};
}
document.getElementById("preview").addEventListener("click", () => {
try {
const draft = buildNotification();
preview.textContent = draft.subject + "\n" + draft.html;
status.textContent = "Local preview only. No email sent.";
} catch {
preview.textContent = "";
status.textContent = "Check the enquiry type and message length.";
}
});Gammal Tech documents service-side HTML sanitization, but your application should still construct a narrow message format. Escaping here is for text inside the paragraph, not arbitrary HTML attributes, URLs or scripts. Preview the prepared HTML as literal text so it cannot become executable page markup.
Finish the no-cost acceptance exercise first
| Case | Expected result | Evidence |
|---|---|---|
| Approved type and 10 to 500 characters | A fixed subject and a prepared message | Local preview screenshot |
A note containing <b>demo</b> | Encoded characters in the prepared HTML | No bold user content in the preview |
| Missing or very short message | Validation message and empty preview | No prepared request |
| An unapproved type supplied locally | Builder rejection | No subject or body sent |
Use fictional wording such as “Please discuss a training website next week.” Your preview proves message construction; it does not prove delivery. Mark the live-send result “not executed” when you stop here.
Add one explicit paid-send path if authorized
For an optional live exercise, deploy to your registered HTTPS domain, load the documented SDK before this handler and keep the earlier builder in scope:
<script src="https://api.gammal.tech/sdk-web.js"></script>const send = document.getElementById("send");
const consent = document.getElementById("cost-consent");
send.addEventListener("click", async () => {
if (send.disabled) return;
if (!consent.checked) {
status.textContent = "Owner approval and cost consent required.";
return;
}
let draft;
try { draft = buildNotification(); }
catch {
status.textContent = "Check the enquiry type and message length.";
return;
}
send.disabled = true;
status.textContent = "Submitting one request.";
try {
const accepted = await GammalTech.notify(draft.subject, draft.html);
status.textContent = accepted === true
? "Accepted for delivery. Check the owner’s inbox."
: "Delivery request not confirmed. Check before trying again.";
} catch {
status.textContent = "Request not confirmed. Check before trying again.";
}
// Remain disabled until the owner checks the result.
});The return value true means accepted for delivery. It does not mean the owner has read the message. The button remains disabled after the request, including an uncertain outcome, pending an owner check. There is no application retry loop. Do not change the catch branch to send another notification reporting its own failure.
Explain what the interface does not enforce
The documented limits are 10 notifications per hour per visitor and 120 per hour per project. These are provider limits, while your short fields and disabled button are local interface behavior. A person can change browser code or reload the page, so the button is not a complete abuse-prevention mechanism.
Keep a named owner responsible for reviewing expected volume and project spending. A worksheet can estimate delivered-notification cost as delivered count multiplied by 0.40 EGP; 25 delivered notifications would cost 10 EGP under the stated rate. Other services are excluded. If the intended workflow needs a broader messaging or customer-email system, assess that requirement separately rather than misrepresenting this fixed-recipient function.
Define useful alerts and uncertain outcomes
A notification helps route a request; it is not authority for sensitive decisions. A message saying “payment complete” is not proof of payment; a visitor can write those words. Do not grant access, dispatch goods or change administrator permissions because of the message body.
Write a short operator note: which subject identifies the learning exercise, who checks the mailbox, and what happens if the request is not confirmed. Avoid claiming guaranteed delivery time or inventing a delivery-status endpoint. When checking an optional live result, record the owner’s confirmation without copying their mailbox contents or credentials into your portfolio.
Hand over a clear notification worksheet
Submit the form, builder, preview results, cost calculation and a state diagram in words: draft, invalid, ready, submitting, accepted or uncertain. Add the live-send status honestly, including “not executed” when appropriate. Explain the fixed recipient and why your code has no email-address field.
Acceptance requires escaped visitor text, bounded inputs, clear cost awareness, no automatic repeat send and messages that distinguish acceptance from confirmed receipt. List the project owner and support process by role rather than private account details.
Common questions
Can this API email any customer address?
No. The documented notify function sends only to the project owner’s verified email. It is not a general-purpose bulk mail or visitor auto-reply API.
Must I pay to complete the lesson?
No. The local builder and preview complete the core exercise without sending. A live notification is optional, requires approval and costs 0.40 EGP per delivered email at the stated date.
Lesson references
- Gammal Tech API documentation and pricing
- Gammal Tech notification contract
- OWASP XSS Prevention Cheat Sheet
Documentation checked on 12 October 2026. Recheck current documentation before implementing or quoting a client project.