Skip to main content

Authentication & API Keys

๐Ÿ” Authentication & Configurationโ€‹

Authentication Method: API Key (Master + Subuser API Keys)

Required Environment Variables:

VariableDescriptionRequired
SENDGRID_API_KEYMaster Sendgrid API keyโœ…
SENDGRID_DEFAULT_DOMAIN_IDDefault domain ID for subusersโœ…
SENDGRID_EVENT_WEBHOOK_URLWebhook URL for email eventsโœ…
SENDGRID_INBOUND_WEBHOOK_URLWebhook URL for inbound emailsโœ…
CONVERSATION_SOCKETConversation socket service URLโœ…
APP_SECRETJWT secret for socket authenticationโœ…

Example Configuration:

SENDGRID_API_KEY=SG.abc123...
SENDGRID_DEFAULT_DOMAIN_ID=12345678
SENDGRID_EVENT_WEBHOOK_URL=https://api.dashclicks.com/v1/integrations/sendgrid/webhook
SENDGRID_INBOUND_WEBHOOK_URL=https://api.dashclicks.com/v1/integrations/sendgrid/webhook/inbound
CONVERSATION_SOCKET=http://localhost:6001
APP_SECRET=your_jwt_secret

Credential Storage: Master API key in environment, subuser API keys in integrations-sendgrid-keys collection

๐Ÿ”‘ API Key Managementโ€‹

Get API Keyโ€‹

Endpoint: GET /v1/integrations/sendgrid/api-key

Purpose: Retrieve existing Sendgrid API key for an account

Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"_id": "507f1f77bcf86cd799439011",
"account_id": "507f191e810c19729de860ea",
"api_key": "SG.xyz123..."
}
}

Add API Keyโ€‹

Endpoint: POST /v1/integrations/sendgrid/api-key

Purpose: Generate new Sendgrid API key for a subuser

Process:

  1. Get subuser from integrations-sendgrid-subusers

  2. Check if API key already exists (return 403 if exists)

  3. Create API key via Sendgrid API:

    POST https://api.sendgrid.com/v3/api_keys
    Authorization: Bearer {SENDGRID_API_KEY}
    On-Behalf-Of: {subuser_username}

    {
    "name": "Dashboard Access"
    }
  4. Store in integrations-sendgrid-keys collection

  5. Create webhook for subuser via WebHookModel.createWebhook()

Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"_id": "507f1f77bcf86cd799439011",
"account_id": "507f191e810c19729de860ea",
"api_key": "SG.xyz123..."
}
}

Error Scenarios:

// API key already exists
{
"success": false,
"errno": 403,
"message": "Sendgrid Api Key already exist"
}

// No subuser found
{
"success": false,
"errno": 403,
"message": "No subuser associated with this account"
}
```## ๐Ÿ›ก๏ธ Middleware

### getSendgridApiKey

**Purpose**: Validate and attach Sendgrid API key to request

**Location**: `Middleware/getSendgridApiKey.js`

**Usage**:

```javascript
router.post('/mail', verifyAuthorization(), getSendgridApiKey, (req, res) => {
// req.sendgrid_api_key available here
});

Logic:

  1. Extract account_id from req.auth
  2. Query SendgridCollection.getApiKey({ account_id })
  3. If not found:
    {
    "success": false,
    "errno": 403,
    "message": "Sendgrid api key is misconfigured.",
    "error": "SENDGRID_API_MISCONFIGURED"
    }
  4. If found: Attach req.sendgrid_api_key
  5. Call next()

Implementation:

const getSendgridApiKey = async (req, res, next) => {
try {
const { account_id } = req.auth;

const { isEmpty, doc } = await SendgridCollection.getApiKey({ account_id });

if (isEmpty) {
throw {
message: 'Sendgrid api key is misconfigured.',
error: 'SENDGRID_API_MISCONFIGURED',
errno: 403,
};
}

req.sendgrid_api_key = doc.api_key;
next();
} catch (error) {
res.status(error.errno || 500).json({
success: false,
errno: error.errno || 500,
message: error.message,
error: error.error,
});
}
};

๐Ÿ“ฆ SendgridCollection Modelโ€‹

Purpose: API key management in database

Methods:

getApiKey() Methodโ€‹

SendgridCollection.getApiKey({ account_id });
// Returns: { isEmpty: boolean, doc: { id, account_id, api_key } }

Query:

db.getCollection('integrations.sendgrid.keys').findOne({
account_id: ObjectId(account_id),
});

addApiKey() Methodโ€‹

SendgridCollection.addApiKey({
account_id,
api_key,
});
// Returns: { id, account_id, api_key }

Creates Document:

{
_id: new ObjectId(),
account_id: ObjectId(account_id),
api_key: "SG.xyz123..."
}

๐ŸŽ›๏ธ ApiKeyControllerLocation: Controllers/ApiKeyController.jsโ€‹

getApiKey() Controllerโ€‹

Route: GET /v1/integrations/sendgrid/auth

Logic:

  1. Extract account_id from req.auth
  2. Call SendgridCollection.getApiKey({ account_id })
  3. Return API key document

Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"_id": "507f1f77bcf86cd799439011",
"account_id": "507f191e810c19729de860ea",
"api_key": "SG.abc123..."
}
}

Error:

{
"success": false,
"errno": 400,
"message": "Record Not found"
}

addApiKey()โ€‹

Route: POST /v1/integrations/sendgrid/auth

Logic:

  1. Get subuser from account document

  2. Check if API key already exists:

    const existingKey = await SendgridCollection.getApiKey({ account_id });
    if (!existingKey.isEmpty) {
    throw { message: 'Sendgrid Api Key already exist', errno: 403 };
    }
  3. Create API key via Sendgrid API (on-behalf-of subuser):

    const response = await this.client.request({
    method: 'POST',
    url: '/v3/api_keys',
    body: {
    name: 'Dashboard Access',
    },
    headers: {
    'on-behalf-of': subuser.username,
    },
    });
  4. Store in database:

    const apiKey = await SendgridCollection.addApiKey({
    account_id: account_id,
    api_key: response.body.api_key,
    });
  5. Create webhook:

    await WebHookModel.createWebhook({
    account_id,
    api_key: response.body.api_key,
    });

Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"_id": "507f1f77bcf86cd799439011",
"account_id": "507f191e810c19729de860ea",
"api_key": "SG.xyz123..."
}
}

Route Configuration (Routes/apikey.js):

const router = require('express').Router();
const ApiKeyController = require('../Controllers/ApiKeyController');
const { verifyAuthorization } = require('../../../Middleware/verifyAuthorization');

router.get('/', verifyAuthorization(), ApiKeyController.getApiKey);
router.post('/', verifyAuthorization(), ApiKeyController.addApiKey);

module.exports = router;

Mounted in index.js:

app.use('/auth', require('./Routes/apikey'));

๐Ÿšจ Error Handlingโ€‹

Common Error Scenarios:

ErrorScenarioHTTP StatusHandling
403No API key found403User must create API key first
403API key already exists403Return error message
403No subuser associated403Must create subuser first
403API key misconfigured403Middleware blocks request
500Sendgrid API call failed500Return Sendgrid error message
400Invalid account_id in token400JWT verification failed

๐Ÿงช Testingโ€‹

Test API Key Retrievalโ€‹

curl -X GET http://localhost:5003/v1/e/sendgrid/auth \
-H "Authorization: Bearer {jwt_token}"

Expected Response (if exists):

{
"success": true,
"message": "SUCCESS",
"data": {
"_id": "507f1f77bcf86cd799439011",
"account_id": "507f191e810c19729de860ea",
"api_key": "SG.xyz123..."
}
}

Test API Key Creationโ€‹

curl -X POST http://localhost:5003/v1/e/sendgrid/auth \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json"

Expected Response:

{
"success": true,
"message": "SUCCESS",
"data": {
"_id": "507f1f77bcf86cd799439011",
"account_id": "507f191e810c19729de860ea",
"api_key": "SG.xyz123..."
}
}

Test Middlewareโ€‹

// Test that middleware blocks requests without API key
const account = await Account.create({
sendgrid: {
subuser: { username: 'test_subuser' },
},
});

// No API key in database
const response = await request(app)
.post('/v1/e/sendgrid/mail')
.set('Authorization', `Bearer ${jwtToken}`)
.send({ subject: 'Test' });

expect(response.status).toBe(403);
expect(response.body.error).toBe('SENDGRID_API_MISCONFIGURED');

โš ๏ธ Important Notesโ€‹

  • ๐Ÿ”‘ Master vs Subuser Keys: Master key in environment only, subuser keys in database
  • ๐Ÿ›ก๏ธ Middleware Protection: All email-sending routes protected by getSendgridApiKey
  • ๐Ÿšซ Duplicate Prevention: Checks for existing API key before creating new one
  • ๐Ÿ”— Webhook Auto-Setup: Creating API key automatically configures event webhook
  • ๐Ÿ“Š One Key Per Account: Each DashClicks account gets exactly one Sendgrid API key
  • ๐Ÿ”„ Key Rotation: No automatic rotation - must delete and recreate manually
  • ๐Ÿ‘ฅ Subuser Requirement: Cannot create API key without existing subuser
  • ๐Ÿ“ Name Convention: All API keys created with name "Dashboard Access"
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM