Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
Developer Handover Pack
Software Licence Management Automation
[YourCompany.com] · IT Department · Prepared by FullSpec · [Today's Date]
This document is the complete technical reference for the Software Licence Management automation build. It covers the current-state process map, agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The FullSpec team uses this document to guide every build and configuration decision. You and your team should use it to review scope, confirm credentials, and track what must be in place before each build stage can begin.
01Process snapshot
02Agent specifications
Connects to Microsoft 365 and Notion each day to retrieve the current list of active and disabled user accounts, along with their assigned licence seats. It compares this live data against the Notion licence register, identifies any seat still assigned to a deactivated user, and surfaces any licence whose renewal date falls within the next 60 days. All findings are written back to Notion as structured anomaly records, ready for the Renewal Coordination Agent to act on. No outbound alerts or finance actions are taken by this agent. Estimated build time: 14 hours. Complexity: Moderate.
// Input
Microsoft365.users.list -> { user_id, display_name, account_enabled, assigned_licences[] }
Microsoft365.licences.list -> { sku_id, sku_part_number, consumed_units, prepaid_units }
Notion.licence_register -> { notion_page_id, tool_name, owner_name, renewal_date, seat_count, status }
// Processing
JOIN users ON licence_register.tool_name WHERE account_enabled = false AND licence_assigned = true
FILTER licence_register WHERE renewal_date <= TODAY + 60 days
CLASSIFY each record as: INACTIVE_USER_ANOMALY | UPCOMING_RENEWAL | CLEAN
// Output
Notion.licence_register -> UPDATE: { anomaly_flag, anomaly_type, flagged_date, renewal_window_days }
Passes structured anomaly list to Renewal Coordination Agent via Notion database trigger- Microsoft 365 Graph API requires at minimum the User.Read.All and Directory.Read.All delegated or application permissions. Confirm with the client whether a service account with Licence Admin role or Global Admin credentials will be used. Global Admin is preferred for reliable scope coverage but must be reviewed against the client's least-privilege policy.
- The Notion integration token must have read and write access to the licence register database. The database schema must include the fields: tool_name (title), owner_name (rich text), renewal_date (date), seat_count (number), anomaly_flag (checkbox), anomaly_type (select), flagged_date (date), renewal_window_days (number), and status (select). Confirm the Notion workspace tier supports API access (Plus plan or above).
- The daily schedule trigger time (07:00 AM) must be confirmed against the client's timezone. The automation platform should store this as a UTC offset and not a named timezone string to avoid daylight saving edge cases.
- The deactivation webhook from Microsoft 365 uses the Graph API Change Notifications endpoint. The automation platform must expose a public HTTPS webhook URL. Confirm this is available before build. The webhook subscription expires after a maximum of 4230 minutes and must be renewed automatically by the orchestration layer.
- Dedupe logic: if an anomaly record for the same tool and the same anomaly type was written to Notion within the previous 24 hours, the agent must not create a duplicate record. Use the Notion page ID as the deduplication key.
- Fallback behaviour: if the Microsoft 365 API returns a 429 (rate limited) or 5xx response, the agent must retry with exponential backoff (1 min, 2 min, 4 min) and log the failure to the error table before exiting gracefully. The Renewal Coordination Agent must not be triggered on a failed or incomplete run.
- Confirm before build: whether the client's Microsoft 365 tenant has conditional access policies that block service account API calls from outside the corporate network. If so, a trusted IP allowlist or managed identity may be required.
Triggered whenever the Licence Discovery Agent writes a new flagged anomaly or upcoming renewal to the Notion register. It reads the flagged records, constructs a structured Slack message to the nominated tool owner, and waits for a response. Responses received via Slack or a linked fallback form are captured and written back to the Notion record. Once a decision is confirmed, the agent updates the licence register with the outcome, posts a consolidated finance note to the relevant Xero contact or bill record, and sends a daily digest to the IT Manager in Slack summarising all actions taken and any alerts still awaiting a response. Estimated build time: 18 hours. Complexity: Moderate.
// Input
Notion.licence_register -> { notion_page_id, tool_name, owner_name, owner_slack_id, anomaly_type,
renewal_date, seat_count, renewal_window_days, flagged_date }
// Processing
FOR EACH flagged record:
Slack.chat.postMessage -> owner_slack_id, message: renewal_alert_template(tool_name, renewal_date, seat_count)
SET response_deadline = flagged_date + 48 hours
POLL Slack.conversations.replies OR form_webhook for owner_response
IF no response by deadline: re-send Slack reminder, log as PENDING_RESPONSE
ON response: parse decision as RENEW | REDUCE(new_seat_count) | CANCEL
// On approval / response received
Notion.licence_register -> UPDATE: { decision, new_seat_count, decision_date, next_review_date }
Xero.contacts OR Xero.bills -> POST finance_note: { tool_name, decision, seat_change, effective_date }
// Output
Slack.chat.postMessage -> IT_manager_channel, daily_digest: {
renewed: [ { tool_name, new_seat_count, decision_date } ],
cancelled: [ { tool_name, effective_date } ],
pending: [ { tool_name, owner_name, alert_sent_date } ],
anomalies_pending_manual_review: [ { tool_name, anomaly_type } ]
}- Slack app must be installed to the client's workspace with the following OAuth scopes: chat:write, channels:read, users:read, users:read.email, conversations.replies:read. The app must be invited to the IT Manager digest channel and have permission to DM individual tool owners. Confirm all tool owners are active members of the same Slack workspace before build. If any owner is external, a fallback form link (hosted on a public URL) must be appended to the Slack message.
- Slack response parsing must handle freeform text and structured button responses. Implement at minimum three quick-reply buttons in the Slack message block: Renew, Reduce Seats, Cancel. Freeform replies should be parsed for keywords (renew, cancel, reduce, drop, keep) and flagged for human review if no keyword is matched.
- The response deadline window is 48 hours. After 48 hours with no response, send one automated reminder. After a further 24 hours with no response, escalate to the IT Manager digest as PENDING and do not send further automated Slack messages. Log the escalation event in the error table.
- Xero integration uses the Accounting API with the openid, profile, email, accounting.transactions, and accounting.contacts OAuth 2.0 scopes. Finance notes should be posted as a manual journal memo or a note on the relevant Xero contact record, not as a bill or invoice, unless the client explicitly requests bill-level write access. Confirm the Xero organisation ID and the correct contact record naming convention before build.
- Notion write-back must update only the fields listed in the IO contract. The agent must not overwrite the tool_name, owner_name, or original renewal_date fields, as these are the source of truth set during initial register configuration.
- The daily digest Slack message must be sent at a fixed time each day (suggested 08:30 AM, configurable) regardless of whether any renewals were actioned that day. If there are no flagged items, the digest should send a clean confirmation message rather than sending nothing, so the IT Manager can distinguish a quiet day from a broken workflow.
- Dedupe logic: the agent must check whether a Slack alert has already been sent for a given notion_page_id within the current 48-hour window before sending. Use the notion_page_id and alert_sent_date combination as the deduplication key.
- Confirm before build: whether the client's Xero account uses single-currency or multi-currency mode, as the finance note payload structure differs. Also confirm whether the IT Manager wants the Xero note posted per-licence or as a single consolidated batch per daily run.
03End-to-end data flow
// ============================================================
// TRIGGER: Daily schedule at 07:00 AM UTC+offset
// OR: Microsoft 365 Graph API Change Notification (user deactivated)
// ============================================================
// ----- LICENCE DISCOVERY AGENT -----
// Step 1: Pull live user data from Microsoft 365
GET https://graph.microsoft.com/v1.0/users
-> fields: id, displayName, accountEnabled, assignedLicenses[].skuId
GET https://graph.microsoft.com/v1.0/subscribedSkus
-> fields: skuId, skuPartNumber, consumedUnits, prepaidUnits.enabled
// Step 2: Pull current Notion licence register
POST https://api.notion.com/v1/databases/{licence_register_db_id}/query
-> fields: notion_page_id, tool_name, owner_name, owner_slack_id,
renewal_date, seat_count, status, anomaly_flag
// Step 3: Cross-reference and classify
FOR EACH notion_record:
IF matched_m365_user.accountEnabled = false AND licence_assigned = true:
-> anomaly_type = 'INACTIVE_USER_ANOMALY'
IF renewal_date <= TODAY + 60 days AND status != 'CANCELLED':
-> anomaly_type = 'UPCOMING_RENEWAL'
ELSE:
-> anomaly_type = 'CLEAN' (no write, no trigger)
// Step 4: Write anomalies to Notion
PATCH https://api.notion.com/v1/pages/{notion_page_id}
-> body: { anomaly_flag: true, anomaly_type, flagged_date: TODAY,
renewal_window_days: days_until_renewal }
// ---- AGENT HANDOFF: Notion database trigger fires ----
// Condition: anomaly_flag = true on any licence_register record
// Passed fields: notion_page_id, tool_name, owner_name, owner_slack_id,
// anomaly_type, renewal_date, seat_count, renewal_window_days
// ----- RENEWAL COORDINATION AGENT -----
// Step 5: Dedupe check
IF alert already sent for notion_page_id within last 48 hours -> SKIP
// Step 6: Send Slack renewal alert to tool owner
POST https://slack.com/api/chat.postMessage
-> channel: owner_slack_id
-> blocks: [ tool_name, renewal_date, seat_count, buttons: [RENEW, REDUCE, CANCEL] ]
-> metadata: { notion_page_id, alert_sent_timestamp }
// Step 7: Collect response
POLL https://slack.com/api/conversations.replies OR form_webhook_url
-> extract: decision (RENEW | REDUCE | CANCEL), new_seat_count (if REDUCE)
-> timeout: 48 hours -> send reminder
-> timeout: 72 hours -> escalate to IT Manager digest, log PENDING_RESPONSE
// Step 8: Write decision back to Notion
PATCH https://api.notion.com/v1/pages/{notion_page_id}
-> body: { decision, new_seat_count, decision_date: TODAY,
next_review_date: TODAY + 90 days, anomaly_flag: false }
// Step 9: Post finance note to Xero
GET https://api.xero.com/api.xro/2.0/Contacts?where=Name=="{tool_vendor_name}"
-> extract: ContactID
POST https://api.xero.com/api.xro/2.0/Contacts/{ContactID}/Notes (or ManualJournals)
-> body: { Details: 'Licence decision: {tool_name}, Decision: {decision},
Seat change: {seat_count} -> {new_seat_count}, Effective: {decision_date}' }
// ---- AGENT HANDOFF: daily digest assembly ----
// Aggregates all records actioned in current 24-hour window
// Fields: tool_name, decision, new_seat_count, decision_date (per resolved record)
// tool_name, owner_name, alert_sent_date (per PENDING record)
// tool_name, anomaly_type (per manual-review exception)
// Step 10: Send IT Manager daily digest via Slack
POST https://slack.com/api/chat.postMessage
-> channel: it_manager_digest_channel_id
-> blocks: [ renewed[], cancelled[], pending[], anomalies_pending_manual_review[] ]
-> scheduled_time: 08:30 AM daily (UTC+offset)
// ============================================================
// END OF AUTOMATED FLOW
// Manual step retained: deprovisioning of users inside platforms
// IT Manager reviews digest and acts on exceptions flagged in Notion
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Software Licence Management.