Corpland ID

Internal API

Server to server calls for product backends, guarded by the shared internal key.

Product backends call these with x-internal-key: <INTERNAL_API_KEY>. Never send the key from a browser.

GET /internal/ventures/:id/members/:userId

Is this user an active member, and with what role. Cached in Redis under the id: prefix and cleared whenever a role or membership changes, so it is safe to call on every request.

{
  "SUCCESS": true,
  "data": {
    "ventureId": "6aca9bfeb50f1fec97df56d6",
    "userId": "6aca9bf9b50f1fec97df56c0",
    "role": "owner",
    "productRoles": [],
    "status": "active",
    "venture": { "_id": "6aca9bfeb50f1fec97df56d6", "name": "Acme Studios", "slug": "acme-studios", "logo": null, "ownerId": "6aca9bf9b50f1fec97df56c0" }
  }
}

Not a member answers 404 with the message "You are not a member of this venture." and the code NOT_A_MEMBER. Treat any 4xx as no access and a 5xx or timeout as unavailable.

GET /internal/ventures/:id/members

Every active member with name, email and role. Use it to show a team picker or to fan out notifications.

GET /internal/users/:userId/ventures

The ventures a user belongs to, the same shape as GET /ventures for that user.

Example middleware

const membership = async (ventureId, userId) => {
  const res = await fetch(`${process.env.CORPLAND_ID_API_URL}/internal/ventures/${ventureId}/members/${userId}`, {
    headers: { "x-internal-key": process.env.CORPLAND_ID_INTERNAL_KEY },
  });
  if (!res.ok) return null;
  return (await res.json()).data;
};

const requireVenture = async (req, res, next) => {
  const ventureId = req.headers["x-venture-id"] || req.user.vid;
  const member = ventureId && (await membership(ventureId, req.user._id));
  if (!member) return res.status(403).json({ message: "Pick a venture you belong to." });
  req.venture = member;
  next();
};

On this page