Back to Software Licence Management

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

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

Step
Name
Description
1
Pull Subscription List from Finance Records
IT Manager downloads or requests active software subscriptions from Xero or credit card records. Done monthly or on demand. Time cost: 30 min/cycle.
2 BOTTLENECK
Cross-Reference Against Known Tools
The subscription list is manually compared to the Notion licence register. Gaps and discrepancies are noted. This step is skipped when the register is out of date, which is most of the time. Time cost: 45 min/cycle.
3
Check User Lists in Each Platform
The Microsoft 365 admin portal is opened and active user lists are reviewed to find inactive employees still holding a seat. Time cost: 40 min/cycle.
4 BOTTLENECK
Identify Upcoming Renewals
Renewal dates in the spreadsheet are compared to today's date to find licences expiring within 60 days. Skipped entirely when the register is stale. Time cost: 20 min/cycle.
5 BOTTLENECK
Assess Usage for Each Upcoming Renewal
The IT Manager logs into each tool manually to check last-login dates or activity reports and decides whether each licence is still needed. Time cost: 50 min/cycle.
6
Email Tool Owners for Confirmation
Individual emails are sent to tool owners asking whether each subscription should be renewed, reduced, or cancelled. Responses arrive over days. Time cost: 25 min/cycle.
7
Chase Non-Responses
Non-responders are followed up via Slack or email, often requiring two or three rounds before a decision is reached. Time cost: 20 min/cycle.
8
Update the Licence Register
Once decisions are collected, the Notion register is updated with new renewal dates, seat changes, or cancellation notes. Time cost: 20 min/cycle.
9
Notify Finance of Changes
A summary of licences to cancel or resize is sent to the finance team for Xero payment adjustments. Time cost: 15 min/cycle.
10
Deprovision Removed Users
Inactive users are manually removed from each platform one by one. Easy to miss for lower-profile subscriptions. Time cost: 30 min/cycle.
Time cost summary: Total manual time per cycle is 295 minutes (approximately 4.9 hours). At a weekly cadence this equates to approximately 5 hours/week and roughly 250 hours/year. Steps 1 through 9 are replaced by the two automation agents described in Section 02. Step 10 (deprovisioning) remains a manual confirmation step by default and is the only step the automation does not replace without explicit client approval of write-back access.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Licence Discovery Agent

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.

Trigger
Daily schedule at 07:00 AM (configurable), or immediately when a user account is deactivated in Microsoft 365 via webhook event subscription.
Tools
Microsoft 365 (Graph API), Notion (Databases API)
Replaces steps
Steps 1, 2, 3, 4
Estimated build
14 hours, Moderate complexity
// 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.
Renewal Coordination Agent

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.

Trigger
Notion database trigger: fires when a licence_register record is updated with anomaly_flag = true by the Licence Discovery Agent.
Tools
Notion (Databases API), Slack (Web API, chat.postMessage, conversations.replies), Xero (Accounting API)
Replaces steps
Steps 5, 6, 7, 8, 9
Estimated build
18 hours, Moderate complexity
// 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.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with agent handoffs and field names
// ============================================================
// 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
// ============================================================
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool configured with one workflow per agent (Licence Discovery Agent workflow, Renewal Coordination Agent workflow). Both workflows share a single credential store. No build platform name is committed at this stage. The orchestration layer must support scheduled triggers, webhook triggers, HTTP request nodes, and Notion and Slack native nodes or HTTP integrations.
Webhook configuration
Two inbound webhook endpoints are required. (1) Microsoft 365 Graph API Change Notification endpoint: must be a publicly reachable HTTPS URL, respond with HTTP 200 within 10 seconds to pass validation, and include a client state secret for request verification. The webhook subscription lifecycle (max 4230 min) must be auto-renewed by a scheduled maintenance workflow running every 3 days. (2) Slack interactivity endpoint: must receive and acknowledge Slack block action payloads (button clicks from renewal alert messages) within 3 seconds and process asynchronously.
Templating approach
Slack message blocks use a shared JSON template stored as a reusable component in the orchestration layer. Variables injected at runtime: tool_name, renewal_date, seat_count, renewal_window_days, notion_page_id (passed as block metadata for response correlation). Xero finance note body uses a plain-text template with the same runtime variables. Notion page patches use a field-map object defined once and referenced by both agents to prevent schema drift.
Error logging
All agent errors, retries, and escalation events are written to a dedicated Supabase table (table name: licence_automation_errors) with fields: event_id (uuid), agent_name, error_type, affected_notion_page_id, error_message, retry_count, resolved (boolean), created_at (timestamp). On any unresolved error after maximum retries, a Slack alert is posted to the IT Manager digest channel with error_type and affected_notion_page_id. The FullSpec team monitors this table during the parallel run period. Contact support@gofullspec.com for any production error escalation.
Testing approach
All agent logic is built and validated in sandbox environments first: Microsoft 365 developer tenant, Notion test database (duplicated from the production register schema), Slack test workspace, and Xero demo company. No writes to production Notion, Slack, or Xero occur until the QA parallel run in weeks 4 to 5. Sandbox credential sets are stored separately from production credentials in the credential store and are labelled explicitly (e.g. NOTION_TOKEN_SANDBOX vs NOTION_TOKEN_PROD).
Estimated total build time
Licence Discovery Agent: 14 hours. Renewal Coordination Agent: 18 hours. End-to-end integration testing, parallel run validation, and error logging setup: 12 hours. Total: 44 hours across 5 delivery weeks.
Credential prerequisites that must be confirmed before build begins: (1) Microsoft 365 service account credentials with Licence Admin or Global Admin role and confirmation of any conditional access restrictions. (2) Notion integration token with read/write access to the production licence register database, and confirmation that the workspace is on a Plus plan or above. (3) Slack app credentials (client ID, client secret, signing secret) with the required OAuth scopes listed in Section 02, and the Slack channel ID for the IT Manager digest. (4) Xero OAuth 2.0 client credentials, the target organisation ID, and the agreed write scope (contact notes vs. manual journals). If any of these are not available at build start, the corresponding agent build stage will be blocked. Raise blockers immediately with the FullSpec team at support@gofullspec.com.
Component
Credential / Config Key
Source
Status
Microsoft 365 Graph API
M365_CLIENT_ID, M365_CLIENT_SECRET, M365_TENANT_ID
Client IT Admin
To confirm
Microsoft 365 Webhook
M365_WEBHOOK_SECRET (client state)
Generated at build
To generate
Notion Databases API
NOTION_TOKEN_PROD, LICENCE_REGISTER_DB_ID
Client Notion Admin
To confirm
Slack Web API
SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_SIGNING_SECRET, IT_MANAGER_CHANNEL_ID
Client Slack Admin
To confirm
Xero Accounting API
XERO_CLIENT_ID, XERO_CLIENT_SECRET, XERO_ORG_ID
Client Finance Manager
To confirm
Supabase Error Log
SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY
FullSpec provisioned
FullSpec handles
Orchestration platform
Environment variables store (sandbox + prod sets)
FullSpec provisioned
FullSpec handles
For technical questions about this specification, contact the FullSpec team at support@gofullspec.com. Include the process name (Software Licence Management) and the relevant agent name in your message so the right team member can respond without delay.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Software Licence Management.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View