Intent Hygiene: What to Expose (and What Not To)
When integrating Bissap, it's tempting to map every existing API endpoint to an intent. Don't.
Intents are your public contract with AI agents. Every intent you register is discoverable, callable, and (if you're not careful) a way to leak your app's core business logic.
The Oversharing Trap
Consider a relationship coaching app with 50 proprietary archetypes. Internally, the app has a GET /archetypes endpoint that returns the full catalog. A developer might wire this up as a Bissap intent:
// ❌ DON'T: exposes your entire catalog
"archetype.list": {
requiredScopes: ["read"],
handler: async () => ({
archetypes: allArchetypes, // 50 proprietary items, scraped in one call
}),
}Now any agent with a read key can dump your entire IP in a single request.
The fix isn't complicated. Just don't register it:
// ✅ DO: expose only the user's assigned archetype
"archetype.info": {
requiredScopes: ["read"],
handler: async ({ archetypeId }) => {
return getArchetype(archetypeId); // single item, scoped to what the agent needs
},
}The Checklist
Before registering an intent, ask yourself:
| Question | If no… |
|---|---|
| Would I put this behind a public REST API? | Don't make it an intent |
| Does it return only what this user/agent needs? | Scope it down |
| Could someone reconstruct my catalog/pricing/logic from repeated calls? | Add rate limits or remove it |
| Is the response bounded in size? | Add pagination or trim fields |
Intent Categories & Risk Levels
Think about what each intent does and label it mentally:
read-single: Returns one item scoped to a user. Low risk. ✅read-bulk: Returns a list or catalog. High risk. Ask: does an agent ever need the full list?write: Mutates user data. Medium risk, ensure auth scoping.admin: Internal operations. Never expose as an intent.
Response Projections
Even safe intents can overshare in their response. Return only what agents need:
// ❌ Returns internal fields
handler: async ({ id }) => getFullArchetype(id)
// Includes: scoring_weights, internal_tags, revenue_tier, ...
// ✅ Returns only agent-relevant fields
handler: async ({ id }) => {
const a = getFullArchetype(id);
return {
id: a.id,
name: a.name,
description: a.description,
// scoring_weights, internal_tags, revenue_tier → omitted
};
}What Bissap Enforces
The SDK's security model works at the intent level:
- If you don't register it, it doesn't exist. There's no way to call an unregistered intent. It returns
404 INTENT_NOT_FOUND. - Intent discovery only shows registered intents. Agents can't see what you didn't expose.
- Scopes gate access. Even registered intents are only callable with the right scope.
This means the risk is entirely in your choice of what to register. Bissap locks the door. You decide which doors to build.
Rules of Thumb
- Start minimal. Launch with 2-3 tightly scoped intents. Add more when agents actually need them.
- Scope to the user. If an intent doesn't take a user context, ask why an agent needs it.
- No bulk dumps. If it returns a list, paginate it and cap the page size.
- Project your responses. Strip internal fields before returning.
- Review quarterly. As your app grows, audit which intents are registered and whether they still make sense.
Real Example
A Love Detox-style app might have dozens of internal endpoints. Here's what good intent selection looks like:
| Intent | Why it's safe |
|---|---|
detox.dailyAffirmation | Single item, user-scoped, changes daily |
detox.dayPlan | Scoped to user's current day and archetype |
archetype.info | Single archetype by ID (agent must already know the ID) |
detox.sosSupport | Single response, crisis support text |
journal.addThought | Write-scoped, user's own journal |
And what was removed:
| Why it was removed | |
|---|---|
archetype.list | Dumps entire proprietary catalog (50 items) in one call |
The app still works perfectly without it. Agents get what they need through archetype.info, they just can't scrape the whole menu.