Reviewer-facing document covering company/use-case, why we need
delivery_type=SHORTLINK (brand+language+deliverability), what data
we send (no PII beyond name/email/internal opaque ID), security
posture (token in env, sandbox-by-default with email override
to a single test inbox), CASL compliance, customer experience,
and the planned /gifts/{uuid} redemption polling.
Provided as a single markdown file under docs/ so it can be exported
to PDF for the Giftbit review team.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
215 lines
13 KiB
Markdown
215 lines
13 KiB
Markdown
# Giftbit API Integration — TARGO Communications
|
||
|
||
**Prepared for:** Giftbit Integration Review Team
|
||
**Submitted by:** TARGO Communications (operating as Gigafibre)
|
||
**Company website:** https://www.targo.ca
|
||
**Primary technical contact:** Louis Paul — louis@targo.ca
|
||
**Last updated:** 2026-05-22
|
||
|
||
---
|
||
|
||
## 1 · Company and Use Case
|
||
|
||
TARGO Communications is a Quebec-based fibre-optic Internet Service Provider headquartered in Sainte-Clotilde-de-Châteauguay (Montérégie region). We operate under the consumer-facing brand **Gigafibre** and serve residential and commercial customers across several municipalities in southwestern Quebec.
|
||
|
||
Our intended use of the Giftbit API is a **customer retention and loyalty program**. We send gift-card emails to **existing TARGO customers** as a thank-you and a soft incentive to maintain their service term. Recipients are exclusively current customers in our billing system (ERPNext); we never send to leads, prospects, or purchased lists. All recipients have an active service agreement that includes consent to receive transactional and relationship-related communications from TARGO (Canada CASL implied consent for current business relationships).
|
||
|
||
Typical campaign profile:
|
||
|
||
| Parameter | Value |
|
||
|---|---|
|
||
| Volume per campaign | 150 – 300 recipients |
|
||
| Frequency | 1 – 2 campaigns per year, per region |
|
||
| Face value per gift | CAD 50 – 60 |
|
||
| Brand mix | Canadian retailers — Tim Hortons, Walmart Canada, Home Depot Canada, IGA, Home Hardware (Amazon was intentionally removed from our brand selection to reinforce the "buy local" messaging in our copy) |
|
||
| Recipient profile | Existing residential fibre customers in Quebec |
|
||
|
||
---
|
||
|
||
## 2 · Why We Need the API (vs. Giftbit's Standard Email Delivery)
|
||
|
||
We selected `delivery_type=SHORTLINK` for three reasons that we believe align with Giftbit's intent:
|
||
|
||
1. **Brand and language consistency.** Our customer base is primarily French-speaking. The email a TARGO customer receives needs to be in French, match our visual brand (Plus Jakarta Sans typography, our green `#00C853`, our footer with mailing address and support contact), and read in our informal Quebec voice (tutoyer). Giftbit's default English templates would feel jarring and erode the trust we have with our customers.
|
||
|
||
2. **Deliverability ownership.** Our domain `targo.ca` has SPF/DKIM/DMARC alignment with Mailjet, our ESP. Sending all customer communications through our own validated infrastructure keeps our sender reputation consistent and avoids domain-spoofing risk that recipients might (legitimately) flag.
|
||
|
||
3. **Engagement attribution.** We need to know whether our specific creative succeeded. The data we get from Mailjet's webhook (open, click on the gift CTA specifically, bounce) feeds our internal analytics and informs the next campaign's copy.
|
||
|
||
We do **not** use the API to bulk-create gifts that we then redistribute, resell, or use as marketing prospect bait — every gift is tied 1:1 to a named existing customer record in our ERP at the moment of campaign creation.
|
||
|
||
---
|
||
|
||
## 3 · Technical Implementation Overview
|
||
|
||
### 3.1 · Architecture
|
||
|
||
```
|
||
┌─────────────────────┐
|
||
│ Our ops console │ internal Vue/Quasar SPA at
|
||
│ (CampaignNewPage) │ https://erp.gigafibre.ca/ops
|
||
└──────────┬──────────┘
|
||
│ 1) operator triggers Giftbit campaign create
|
||
▼
|
||
┌─────────────────────┐ create_giftbit_campaign.js
|
||
│ Node script (CLI) │ • POST /papi/v1/campaign with
|
||
│ create_giftbit…js │ delivery_type=SHORTLINK
|
||
└──────────┬──────────┘ • polls /papi/v1/gifts until shortlinks created
|
||
│ 2) writes giftbit-gifts-<id>.csv to disk
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Our hub service │ 3) operator uploads the Giftbit CSV alongside
|
||
│ (Node, Mailjet SMTP)│ a customer-contact CSV; the hub pairs them
|
||
└──────────┬──────────┘ and sends one personalized FR/EN email
|
||
│ per recipient via Mailjet
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Customer's inbox │
|
||
└──────────┬──────────┘
|
||
│ click on CTA → opens Giftbit shortlink
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Giftbit's branded │ recipient chooses brand, redeems on
|
||
│ redemption page │ Giftbit-hosted UI (we never touch the code)
|
||
└─────────────────────┘
|
||
```
|
||
|
||
### 3.2 · API Endpoints Used
|
||
|
||
All requests go to:
|
||
- **Production:** `https://api.giftbit.com/papi/v1`
|
||
- **Sandbox:** `https://api-testbed.giftbit.com/papi/v1`
|
||
|
||
| Method | Path | Purpose |
|
||
|---|---|---|
|
||
| `GET` | `/ping` | Token sanity check at startup of every CLI run |
|
||
| `POST` | `/campaign` | Create a new gift campaign with `delivery_type: "SHORTLINK"` |
|
||
| `GET` | `/gifts?campaign_uuid=…` | Poll for generated shortlinks (Giftbit creates them asynchronously) |
|
||
| `GET` | `/gifts/{uuid}` | **(Planned, see §6)** Periodic redemption-status polling per gift |
|
||
|
||
### 3.3 · `POST /campaign` payload (production-shape)
|
||
|
||
```json
|
||
{
|
||
"id": "targo-2026-q2-loyalty",
|
||
"subject": "Cadeau TARGO — merci",
|
||
"message": "Merci d'être client TARGO. Voici une carte-cadeau pour vous remercier.",
|
||
"expiry": "2026-12-31",
|
||
"price_in_cents": 6000,
|
||
"brand_codes": ["timhortonsca", "walmartca", "homedepotca", "igacaca", "homehardwareca"],
|
||
"contacts": [
|
||
{ "id": "ERPNEXT-CUST-001234", "firstname": "Marie", "lastname": "Tremblay", "email": "marie@example.com" },
|
||
{ "id": "ERPNEXT-CUST-001235", "firstname": "Jean", "lastname": "Bouchard", "email": "jean@example.com" }
|
||
],
|
||
"delivery_type": "SHORTLINK"
|
||
}
|
||
```
|
||
|
||
The `id` field on each contact carries our internal ERPNext Customer ID. This is what lets us reconcile redemption events back to a specific customer record without any PII (postal code, phone, address) ever crossing the API boundary.
|
||
|
||
### 3.4 · What we send / what we don't send
|
||
|
||
| Field sent to Giftbit | Sent? | Notes |
|
||
|---|:---:|---|
|
||
| First name | ✅ | Used only for the shortlink landing page personalization |
|
||
| Last name | ✅ | Same |
|
||
| Email address | ✅ | Required by Giftbit's data model even with SHORTLINK; never used for outbound mail by Giftbit when SHORTLINK is set |
|
||
| Internal customer ID | ✅ | Opaque string, no PII embedded |
|
||
| Phone number | ❌ | Never |
|
||
| Postal address | ❌ | Never |
|
||
| Service plan / billing info | ❌ | Never |
|
||
| Demographics | ❌ | Never |
|
||
|
||
---
|
||
|
||
## 4 · Security Posture
|
||
|
||
### 4.1 · Credential handling
|
||
- Giftbit API token is stored only in the environment of our production hub container (`GIFTBIT_TOKEN` env var, loaded from a `.env` file with `chmod 600`). The token never appears in source code or in our Gitea repository.
|
||
- All API calls are made over HTTPS using Node's native `https` module with default TLS validation. We do **not** disable certificate verification.
|
||
|
||
### 4.2 · Sandbox isolation
|
||
- During all development and pre-flight QA we use `api-testbed.giftbit.com` exclusively (`--sandbox` CLI flag enforces this).
|
||
- When the sandbox flag is set, every contact email in the CSV is overridden to a single internal test inbox (`louis@targo.ca`). This means even if a developer mistakenly uses real customer data while testing, no test gift can be sent to a real customer.
|
||
- Production runs require explicit removal of the `--sandbox` flag and are gated by a manual operator confirmation step in our ops console.
|
||
|
||
### 4.3 · Shortlink integrity
|
||
- Each shortlink is consumed at most once on Giftbit's side. We do not log shortlinks in any plain-text channel (no Slack, no email, no analytics dashboards).
|
||
- The shortlink only travels through:
|
||
1. Our hub's JSON campaign file on disk (filesystem perms `600`, Docker volume not exposed to internet)
|
||
2. The outbound Mailjet email to the named recipient
|
||
3. The Mailjet click webhook event (where Mailjet provides the URL alongside `CustomID = <campaign>:<row>` so we can reconcile without re-exposing the link in logs)
|
||
|
||
### 4.4 · Webhook authenticity
|
||
- We are aware that Mailjet click events carry the original URL. We use this only to distinguish gift-CTA clicks from other link clicks for our analytics; the shortlink itself is **not** echoed back to any external system from our webhook handler.
|
||
|
||
---
|
||
|
||
## 5 · Compliance and Customer Experience
|
||
|
||
### 5.1 · Consent (CASL — Canada Anti-Spam Legislation)
|
||
- 100% of recipients have an active service agreement with TARGO containing implied consent for relationship and transactional messages.
|
||
- Every campaign email includes our physical mailing address and a working `support@targo.ca` contact, satisfying CASL section 6(2)(c) identification requirements.
|
||
- We provide a one-click way to refuse or request a replacement gift by replying to support@targo.ca. Refused gifts are returned to our Giftbit balance (`/gifts/{uuid}` cancellation, planned in §6).
|
||
|
||
### 5.2 · Recipient experience
|
||
1. Customer opens the email in their inbox. Sender is `TARGO <noreply@targo.ca>`, subject "🎁 Un cadeau pour toi, de la part de TARGO" (or English equivalent based on the customer's preferred language flag in ERPNext).
|
||
2. Email displays the customer's first name, the gift face value (e.g. "60 $"), the brand selection (rendered as small inline logos), a single primary CTA button leading to the Giftbit shortlink, and a brief plain-language disclosure of any commitment terms.
|
||
3. On click, the customer lands on Giftbit's hosted redemption page. They select a brand from the campaign's allowed list and follow Giftbit's standard redemption flow. We have no visibility into the redemption form itself.
|
||
4. The customer's record in our system is annotated with `gift_link_clicked = true` and timestamp upon return webhook from Mailjet. Redemption confirmation will (per §6) be polled directly from `/gifts/{uuid}`.
|
||
|
||
### 5.3 · Data retention
|
||
- Campaign JSON files (including shortlinks) are retained on the hub for 24 months for accounting and customer-support traceability, after which they are archived to encrypted cold storage and removed from the live system.
|
||
- We do not share, sell, or otherwise distribute any data collected from this integration to third parties.
|
||
|
||
---
|
||
|
||
## 6 · Planned Next Steps — Redemption Status Polling
|
||
|
||
We intend to add a recurring background poll against `GET /gifts/{uuid}` for each issued shortlink in an active campaign window. The plan:
|
||
|
||
| Cadence | Action |
|
||
|---|---|
|
||
| **T + 1 hour** | First poll for every gift after campaign send completes |
|
||
| **T + 1 day, +7 d, +30 d** | Subsequent polls until either the gift is `REDEEMED` or `expiry` passes |
|
||
| **On expiry + 1 day** | Final reconciliation poll, then move the recipient row to a "closed" state |
|
||
|
||
The polled status (`NEW` / `LINKCREATED` / `REDEEMED` / `REFUNDED`) and the brand the recipient selected will be persisted on the same per-recipient JSON row alongside `gift_link_clicked_at`. This lets us produce a single CSV report covering the full lifecycle:
|
||
|
||
```
|
||
row, firstname, email, gift_url, gift_clicked_at,
|
||
gift_status (REDEEMED/etc), gift_redeemed_at, gift_brand_selected
|
||
```
|
||
|
||
We will respect Giftbit's published rate limits and apply exponential backoff on 429/5xx responses. Polling will be batched (single request returns up to 500 gifts via `?campaign_uuid=…&limit=500`), so even a 1000-recipient campaign costs at most two requests per polling cycle.
|
||
|
||
---
|
||
|
||
## 7 · Source Code References (read-only access available on request)
|
||
|
||
| File | Purpose |
|
||
|---|---|
|
||
| `scripts/campaigns/create_giftbit_campaign.js` | The `POST /campaign` + shortlink-polling CLI that talks to Giftbit |
|
||
| `services/targo-hub/lib/campaigns.js` | Hub service: CSV pairing, Mailjet send, webhook receiver, planned `/gifts/{uuid}` poller |
|
||
| `apps/ops/src/modules/campaigns/` | Internal Vue/Quasar console where operators create and monitor campaigns |
|
||
| `services/targo-hub/templates/gift-email-fr.html` | The French email template (HTML) |
|
||
| `services/targo-hub/templates/gift-email-en.html` | The English email template |
|
||
|
||
Repository (Giftbit reviewers can be added on request): `https://git.targo.ca/louis/gigafibre-fsm`
|
||
|
||
---
|
||
|
||
## 8 · Contacts
|
||
|
||
| Role | Name | Email |
|
||
|---|---|---|
|
||
| Owner / Lead developer | Louis Paul | louis@targo.ca |
|
||
| Customer support (recipients use this) | TARGO Support | support@targo.ca |
|
||
| Mailing address | TARGO Communications, 1867 ch. de la rivière, Sainte-Clotilde, QC, Canada | |
|
||
|
||
We are happy to walk through the code, demonstrate the ops console live, or send a small sample campaign through the sandbox if that would help your review. Please let us know what level of detail or evidence would be most useful.
|
||
|
||
Thank you for your time.
|
||
|
||
— *TARGO Communications · 2026*
|