Data deletion callback
The deauthorize and data deletion callbacks Meta requires, the signed request format, the expected JSON response and how deletion status is reported.
Two distinct callbacks
Meta requires two separate endpoints. They are triggered by different user actions and must not be treated as interchangeable.
- Deauthorize callback
- Fires when a user removes the app from their Meta account. Homa revokes access and deactivates the connection. It does not by itself erase historical records.
- Data deletion callback
- Fires when a user requests deletion of the data the app holds about them. Homa must erase that data and report a status the user can check.
| Purpose | Endpoint |
|---|---|
| Deauthorize callback | https://meta.homacrm.com/api/meta/deauthorize |
| Data deletion callback | https://meta.homacrm.com/api/meta/data-deletion |
| Human-readable instructions | https://meta.homacrm.com/data-deletion |
Signed request format
Both callbacks receive a signed_request parameter: two base64url segments separated by a dot. The first is an HMAC signature, the second is the JSON payload. Verify the signature before trusting any field.
import crypto from 'node:crypto'
function base64UrlDecode(input: string) {
return Buffer.from(input.replace(/-/g, '+').replace(/_/g, '/'), 'base64')
}
function parseSignedRequest(signedRequest: string) {
const [encodedSig, encodedPayload] = signedRequest.split('.')
if (!encodedSig || !encodedPayload) return null
const expected = crypto
.createHmac('sha256', process.env.META_APP_SECRET!)
.update(encodedPayload)
.digest()
const received = base64UrlDecode(encodedSig)
if (
received.length !== expected.length ||
!crypto.timingSafeEqual(received, expected)
) {
return null
}
return JSON.parse(base64UrlDecode(encodedPayload).toString('utf8'))
}Expected response
The data deletion callback must return JSON containing a URL where the user can check the status of their request, and a confirmation code that identifies it.
{
"url": "https://meta.homacrm.com/data-deletion?code=abc123",
"confirmation_code": "abc123"
}export async function POST(request: Request) {
const form = await request.formData()
const signedRequest = form.get('signed_request')
if (typeof signedRequest !== 'string') {
return Response.json({ error: 'Bad request' }, { status: 400 })
}
const payload = parseSignedRequest(signedRequest)
if (!payload?.user_id) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
// Record the request, then erase asynchronously.
const confirmationCode = await enqueueDeletion(payload.user_id)
const statusUrl = new URL('/data-deletion', process.env.NEXT_PUBLIC_SITE_URL)
statusUrl.searchParams.set('code', confirmationCode)
return Response.json({
url: statusUrl.toString(),
confirmation_code: confirmationCode,
})
}Status reporting
The URL returned in the response must remain reachable so a user can confirm what happened. The status page accepts the confirmation code and reports progress.
- Received: the request is recorded and queued.
- In progress: erasure is running across the relevant systems.
- Completed: all in-scope data has been erased, with the completion date shown.
- Retained: specific records are kept under a legal obligation, with the reason stated.
Scope of deletion
- Erased
- Stored access tokens, synced conversations and comments, contact records derived from Meta assets, and cached profile data.
- Retained
- Minimal records required for legal, tax, security or fraud-prevention purposes, plus audit entries proving the deletion itself was performed.
- Out of scope
- Data held by Meta. A user must use Meta’s own tools to remove content from Meta’s platforms; Homa cannot delete it on their behalf.
Backups are handled on their own retention cycle. A record removed from live systems is expunged from backups when those backups age out, and a restored backup is re-filtered against completed deletion requests.
Manual requests
Users who prefer not to use the Meta flow can submit a request directly. The public instructions page provides a form, and the same process and status reporting apply.
- Requests are acknowledged with a reference number.
- Identity is verified before erasure where the request is not signed by Meta.
- The outcome is communicated to the address that submitted the request.