===============
LIBRARY RULES
===============
From library maintainers:
- Exchange the install `code` immediately: it is valid for 1 minute.
- App user keys (client_id/client_secret) come only from POST https://api.sendpulse.com/market-service/oauth/authorize. They are not the API keys from the user's account settings.
- Get user tokens from POST https://api.sendpulse.com/oauth/access_token/market with grant_type=client_credentials, not from /oauth/access_token.
- Store client_id and client_secret per SendPulse user; they stay the same while the app is installed.
- Cache the access token until expires_in elapses; do not request a new token on every API call.
- The Install URL may be called with GET or POST; handle both and read code and lang from the query string.
- The app is rendered in an iframe on https://login.sendpulse.com: set CSP frame-ancestors to allow it, never X-Frame-Options DENY/SAMEORIGIN, and don't depend on third-party cookies.
- Install URL and Uninstall URL must be public HTTPS endpoints.
- Uninstall URL must return 200 OK; the connection is removed regardless of the response, so delete stored keys on receipt.
- Request only the scopes your app actually uses; scopes affect moderation.
- Add https://cdn.sendpulse.com/dist/css/sp-marketplace-app-ui.min.css to the app page
to match SendPulse UI.
- App type (public, private, external integration) cannot be changed after creation.
- Add users to a private app by their SendPulse account email in the App availability tab.
- After the authorization flow, issue your own signed session; never identify users by a plain user_id in the URL.
- Check `result` and numeric `code` in marketplace auth errors, not the error text.
- Stay under 10 API requests per second per account; 429 means the quota is exceeded.
### Setup commands for Node.js + Express SendPulse example
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/examples/node-express/README.md
Install dependencies, copy the environment file, and start the server. Set SENDPULSE_APP_ID and SENDPULSE_APP_SECRET in .env before running.
```bash
npm install
cp .env.example .env # set SENDPULSE_APP_ID and SENDPULSE_APP_SECRET
npm start
```
--------------------------------
### Set up and run the Node.js Express example with ngrok
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/02-quickstart.md
Commands to install dependencies, configure environment variables, start the server on port 3000, and expose it via ngrok. Fill SENDPULSE_APP_ID and SENDPULSE_APP_SECRET in .env after step 3.
```bash
cd examples/node-express
npm install
cp .env.example .env # fill SENDPULSE_APP_ID and SENDPULSE_APP_SECRET after step 3
npm start # listens on :3000
ngrok http 3000 # gives https://.ngrok-free.app
```
--------------------------------
### Install dependencies and start Docker
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
Run composer install to install PHP dependencies, then optionally start Docker Compose for local deployment.
```bash
composer install
docker compose up -d # optional
```
--------------------------------
### Install UI Kit stylesheet
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/10-ui-kit.md
Add this link to the of your app page. No other setup is required.
```html
```
--------------------------------
### Authorization flow diagram for the example server
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/02-quickstart.md
Text diagram showing how the example server exchanges the code for tokens and fetches mailing lists. The flow repeats when clicking View next to the app.
```text
code ──POST /market-service/oauth/authorize──▶ client_id, client_secret (saved)
client_id/secret ──POST /oauth/access_token/market──▶ access_token
access_token ──GET /addressbooks──▶ your mailing lists (rendered on the page)
```
--------------------------------
### Setup and run commands for Python Flask app
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/examples/python-flask/README.md
Install required packages (Flask and requests), set environment variables for SendPulse app ID and secret, generate a session secret, and run the app. The app should be exposed on port 3000 over HTTPS, with Install URL set to https:///install and Uninstall URL to https:///uninstall.
```bash
pip install flask requests
export SENDPULSE_APP_ID=your-app-id
export SENDPULSE_APP_SECRET=your-app-secret
export SESSION_SECRET=$(openssl rand -hex 32)
python app.py
```
--------------------------------
### Minimal app page example
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/10-ui-kit.md
A complete HTML page demonstrating the UI Kit classes for a settings form and a table. Includes the stylesheet link in the head.
```html
My SendPulse app
Settings
Connect your store to SendPulse mailing lists.
Date
Contacts synced
Status
2026-10-14
120
OK
```
--------------------------------
### Install sendpulse/market-rest-api via Composer
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/06-authorization-flow.md
The `sendpulse/market-rest-api` package wraps this flow. See [Custom Telephony Starter Kit](13-telephony-starter-kit.md) for a full Laravel project.
```bash
composer require sendpulse/market-rest-api
```
--------------------------------
### GET /api/boards
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
Returns CRM boards as JSON to check that auth and REST calls work.
```APIDOC
## GET /api/boards
### Description
Returns CRM boards as JSON to check that auth and REST calls work.
### Method
GET
### Endpoint
/api/boards
### Parameters
No parameters documented.
### Response
Returns CRM boards as JSON.
```
--------------------------------
### List mailing lists with Python requests (emails scope)
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/09-calling-the-api.md
This function lists mailing lists using the requests library. It gets a token via get_token(user_id), sends a GET request with a 10-second timeout, and raises an exception for HTTP errors. Returns the JSON response.
```python
def list_mailing_lists(user_id):
r = requests.get(
"https://api.sendpulse.com/addressbooks",
headers={"Authorization": f"Bearer {get_token(user_id)}"},
timeout=10,
)
r.raise_for_status()
return r.json()
```
--------------------------------
### How an app works in 4 calls
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/README.md
Text-based flow showing the four steps from user install to API access, including the uninstall webhook. The code parameter is valid for 1 minute, and the final access token is used as a Bearer token on SendPulse REST API calls.
```text
1. User clicks Install → grants scopes
2. SendPulse → GET|POST https://YOUR_INSTALL_URL/?code={code}&lang=en (in an iframe; code valid 1 min)
3. Your server → POST https://api.sendpulse.com/market-service/oauth/authorize
{ app_id, secret, code } → { client_id, client_secret }
4. Your server → POST https://api.sendpulse.com/oauth/access_token/market
{ client_id, client_secret, grant_type: "client_credentials" } → { access_token, expires_in }
Then: Authorization: Bearer {access_token} on SendPulse REST API calls.
On uninstall: SendPulse → POST https://YOUR_UNINSTALL_URL (user + keys JSON), reply 200 OK.
```
--------------------------------
### Node.js Express handler for uninstall webhook
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/08-uninstall-webhook.md
Example handler that verifies the app_id and client credentials, deletes the user if valid, and always returns 200 OK. It is idempotent: repeated requests for already removed users still return 200.
```js
app.post('/uninstall', express.json(), async (req, res) => {
const { user_id, app_id, client_id, client_secret } = req.body || {};
if (app_id === process.env.SENDPULSE_APP_ID) {
const stored = await loadUserKeys(user_id);
if (stored && stored.client_id === client_id && stored.client_secret === client_secret) {
await deleteUser(user_id);
}
}
res.sendStatus(200);
});
```
--------------------------------
### Success response 200 for token request
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/07-access-token.md
Example of a successful token response. Contains token_type Bearer, expires_in in seconds, and access_token for the Authorization header. Read expires_in from every response; do not hardcode a duration.
```json
{
"token_type": "Bearer",
"expires_in": 7200,
"access_token": "eyJ0eXAiOiJKV1Q...Bv1L"
}
```
--------------------------------
### Handle /install and exchange code in Python
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/06-authorization-flow.md
Flask route that accepts GET or POST, reads the authorization code, calls the SendPulse OAuth authorize endpoint, saves user keys, and redirects with a signed session. Requires APP_ID and APP_SECRET constants.
```python
@app.route("/install", methods=["GET", "POST"])
def install():
code = request.args.get("code") or request.form.get("code")
r = requests.post(
"https://api.sendpulse.com/market-service/oauth/authorize",
json={"app_id": APP_ID, "secret": APP_SECRET, "code": code},
timeout=10,
)
data = r.json()
if not data.get("result"):
return "Authorization failed. Reopen the app from SendPulse.", 400
save_user_keys(data["data"]["user_id"], data["data"]["client_id"], data["data"]["client_secret"])
session = sign_session(data["data"]["user_id"]) # HMAC-signed, expires in ~1 hour
return redirect(f"/app?s={quote(session)}")
```
--------------------------------
### POST /market-service/oauth/authorize
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/06-authorization-flow.md
Exchanges the authorization code obtained in step 1 for app user keys. The response contains client_id and client_secret that remain constant while the app is installed.
```APIDOC
## POST /market-service/oauth/authorize
### Description
Exchanges the authorization code obtained in step 1 for app user keys. The response contains client_id and client_secret that remain constant while the app is installed.
### Method
POST
### Endpoint
https://api.sendpulse.com/market-service/oauth/authorize
### Parameters
#### Request Body
- **app_id** (string) - Required - Your app's App ID.
- **secret** (string) - Required - Your app's Secret key.
- **code** (string) - Required - Code from step 1.
### Request Example
{
"app_id": "9b0f2f98-d75f-4562-887e-2b79bc8a1eee",
"secret": "0d028163-9c84-40e4-8508-f4927badf735",
"code": "21efd6b100b2602cb2d75cb06c2bf37f"
}
### Response
#### Success Response (200)
- **result** (boolean) - Indicates success.
- **data** (object) - Contains the app user keys and connection details.
- **id** (uuid) - Connection ID for this user and app.
- **user_id** (int) - SendPulse user ID. Use it as the primary key of your user table.
- **app_id** (uuid) - Your app ID.
- **client_id** (string) - App user key.
- **client_secret** (string) - App user key.
- **created_at** (datetime) - First connection time.
- **updated_at** (datetime) - Last connection update.
#### Response Example
{
"result": true,
"data": {
"id": "9b0f56dd-4f4d-4792-ae20-23f99659b69e",
"user_id": 7043663,
"app_id": "9b0fdd98-d75f-4562-887e-2b79bc8a1eee",
"client_id": "b2bfd8a8441294fa0171c195ada8cf56",
"client_secret": "f7064fb0bbecd392d7b3ea2e2bd77758",
"created_at": "2024-01-10T15:50:20.000000Z",
"updated_at": "2024-01-10T15:50:20.000000Z"
}
}
#### Error Response (422 Unprocessable Entity)
- **result** (boolean) - Indicates failure.
- **data** (null) - Always null on error.
- **error** (string) - Error message. Note: the first letter of 'Сonnect' may be Cyrillic 'С' (U+0421), so string comparison with 'Connect not found!' fails.
- **code** (int) - Numeric error code.
#### Error Example
{
"result": false,
"data": null,
"error": "Сonnect not found!",
"code": 5
}
### Notes
- The code expires after 1 minute, can be used only once, and app_id/secret must be correct.
- Branch on `result === false` and the numeric `code`, not on the `error` string.
```
--------------------------------
### POST /oauth/access_token/market
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/07-access-token.md
Obtains an access token for a marketplace app user. The token authorizes API requests on behalf of the installing user, limited to the scopes they approved.
```APIDOC
## [POST] /oauth/access_token/market
### Description
Obtains an access token for a marketplace app user. The token authorizes API requests on behalf of the installing user, limited to the scopes they approved.
### Method
POST
### Endpoint
https://api.sendpulse.com/oauth/access_token/market
### Parameters
#### Request Body
- **client_id** (string) - Required - App user key from the authorization flow.
- **client_secret** (string) - Required - App user key from the authorization flow.
- **grant_type** (string) - Required - Always `client_credentials`.
### Request Example
{
"client_id": "b2bfd8a8441294fa0171c195ada8cf56",
"client_secret": "f7064fb0bbecd392d7b3ea2e2bd77758",
"grant_type": "client_credentials"
}
### Response
#### Success Response (200)
- **token_type** (string) - `Bearer`.
- **expires_in** (int) - Lifetime in seconds. Read it from every response; don't hardcode a duration.
- **access_token** (string) - Token for the `Authorization` header.
#### Response Example
{
"token_type": "Bearer",
"expires_in": 7200,
"access_token": "eyJ0eXAiOiJKV1Q...Bv1L"
}
#### Error Response (401 Unauthorized)
- **error** (string) - Error code, e.g., `invalid_client`.
- **error_description** (string) - Description of the error.
- **message** (string) - Error message.
#### Error Response Example
{
"error": "invalid_client",
"error_description": "Client authentication failed",
"message": "Client authentication failed"
}
```
--------------------------------
### Error 401 Unauthorized response
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/07-access-token.md
Example of an error response when client authentication fails. Check the keys; if the user uninstalled the app, the keys are revoked and you must stop calling the API for that user.
```json
{
"error": "invalid_client",
"error_description": "Client authentication failed",
"message": "Client authentication failed"
}
```
--------------------------------
### Call initiation request payload
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
SendPulse CRM sends this JSON payload to POST /custom-telephony/call when a user starts a call. The integrationId and phone fields are required.
```json
{
"integrationId": 123,
"phone": "+380631234567"
}
```
--------------------------------
### List mailing lists with JavaScript fetch (emails scope)
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/09-calling-the-api.md
This async function lists mailing lists using the fetch API. It obtains a token via getToken(userId), sends a GET request to the addressbooks endpoint, and handles a 401 response by invalidating the token and throwing an error. Returns the JSON response.
```javascript
async function listMailingLists(userId) {
const token = await getToken(userId);
const r = await fetch('https://api.sendpulse.com/addressbooks', {
headers: { Authorization: `Bearer ${token}` },
});
if (r.status === 401) { invalidateToken(userId); throw new Error('Token expired or app uninstalled'); }
return r.json();
}
```
--------------------------------
### POST /custom-telephony/call
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
SendPulse CRM calls this endpoint when the user starts a call; your code passes it to the telephony provider. It accepts integrationId and phone, and returns 200 OK on success.
```APIDOC
## POST /custom-telephony/call
### Description
SendPulse CRM calls this endpoint when the user starts a call; your code passes it to the telephony provider.
### Method
POST
### Endpoint
/custom-telephony/call
### Parameters
#### Request Body
- **integrationId** (number) - Required - Solution integration ID.
- **phone** (string) - Required - Phone in international format.
### Request Example
{
"integrationId": 123,
"phone": "+380631234567"
}
### Response
#### Success Response (200)
Returns 200 OK on success.
```
--------------------------------
### Handle /install and exchange code in Node.js
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/06-authorization-flow.md
Express route that accepts GET or POST, reads the authorization code, calls the SendPulse OAuth authorize endpoint, saves user keys, and redirects with a signed session. Requires SENDPULSE_APP_ID and SENDPULSE_APP_SECRET environment variables.
```javascript
app.all('/install', async (req, res) => {
const code = req.query.code || req.body?.code;
const lang = req.query.lang || 'en';
const r = await fetch('https://api.sendpulse.com/market-service/oauth/authorize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_id: process.env.SENDPULSE_APP_ID,
secret: process.env.SENDPULSE_APP_SECRET,
code,
}),
});
const json = await r.json();
if (!json.result) return res.status(400).send('Authorization failed. Reopen the app from SendPulse.');
await saveUserKeys(json.data.user_id, json.data.client_id, json.data.client_secret);
const session = signSession(json.data.user_id); // HMAC-signed, expires in ~1 hour
res.redirect(`/app?s=${encodeURIComponent(session)}&lang=${encodeURIComponent(lang)}`);
});
```
--------------------------------
### Project directory structure
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
The starter kit's Laravel project structure includes app directories for controllers, models, repositories, and requests, plus routes files for web and API.
```text
app/
Controllers/
Models/
Repositories/
Requests/
routes/
web.php
api.php
```
--------------------------------
### Configure .env with SendPulse credentials
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
Create a .env file in the project root with your SendPulse app ID, app secret, and application URL. These values are used for authentication and webhook callbacks.
```dotenv
SENDPULSE_APP_ID=your-app-id
SENDPULSE_APP_SECRET=your-app-secret
APP_URL=https://your-domain.example
```
--------------------------------
### Success response with app user keys
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/06-authorization-flow.md
On success, the response contains the connection ID, user ID, app ID, and the client_id and client_secret app user keys. Save these keys for background operations.
```json
{
"result": true,
"data": {
"id": "9b0f56dd-4f4d-4792-ae20-23f99659b69e",
"user_id": 7043663,
"app_id": "9b0fdd98-d75f-4562-887e-2b79bc8a1eee",
"client_id": "b2bfd8a8441294fa0171c195ada8cf56",
"client_secret": "f7064fb0bbecd392d7b3ea2e2bd77758",
"created_at": "2024-01-10T15:50:20.000000Z",
"updated_at": "2024-01-10T15:50:20.000000Z"
}
}
```
--------------------------------
### POST /uninstall (Uninstall Webhook)
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/08-uninstall-webhook.md
SendPulse sends a POST request to your Uninstall URL when a user uninstalls your app. The request contains the user's connection data. Your app should verify the request, delete stored credentials, and return 200 OK.
```APIDOC
## [POST] /uninstall
### Description
SendPulse sends a POST request to your Uninstall URL when a user uninstalls your app. The request contains the user's connection data. Your app should verify the request, delete stored credentials, and return 200 OK.
### Method
POST
### Endpoint
https://YOUR_UNINSTALL_URL
### Parameters
#### Request Body
- **id** (uuid) - Required - SendPulse user / connection ID.
- **user_id** (int) - Required - SendPulse user ID.
- **app_id** (uuid) - Required - Your app ID.
- **client_id** (string) - Required - App user key being revoked.
- **client_secret** (string) - Required - App user key being revoked.
- **created_at** (datetime) - Required - First connection time.
- **updated_at** (datetime) - Required - Last connection update.
### Request Example
{
"id": "9b0f56dd-4f4d-4792-ae20-23f99659b69e",
"user_id": 7043663,
"app_id": "9b0fdd98-d75f-4562-887e-2b79bc8a1eee",
"client_id": "b2bfd8a8441294fa0171c195ada8cf56",
"client_secret": "f7064fb0bbecd392d7b3ea2e2bd77758",
"created_at": "2024-01-10T15:50:20.000000Z",
"updated_at": "2024-01-10T15:50:20.000000Z"
}
### Response
#### Success Response (200)
Return 200 OK. The connection is removed regardless of your response.
#### Response Example
HTTP/1.1 200 OK
### Notes
- Verify the request: match `app_id` to yours and `client_id` + `client_secret` to the pair you stored. Ignore requests that don't match.
- Make the handler idempotent: a repeated request for an already removed user should still return 200.
- Delete or deactivate the stored `client_id` / `client_secret` and cached tokens for that user.
- Stop background jobs and outgoing webhooks for that user.
```
--------------------------------
### POST /custom-telephony/uninstall
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
Removes the app user. See the Uninstall webhook documentation for more details.
```APIDOC
## POST /custom-telephony/uninstall
### Description
Removes the app user. See [Uninstall webhook](08-uninstall-webhook.md).
### Method
POST
### Endpoint
/custom-telephony/uninstall
### Parameters
No parameters documented.
### Response
No response details provided in the source.
```
--------------------------------
### List mailing lists with curl (emails scope)
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/09-calling-the-api.md
Use this curl command to list mailing lists. It requires an access token stored in the $ACCESS_TOKEN environment variable. The response is a JSON array of mailing lists.
```bash
curl -s https://api.sendpulse.com/addressbooks \
-H "Authorization: Bearer $ACCESS_TOKEN"
```
--------------------------------
### Cache token in JavaScript with fetch
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/07-access-token.md
JavaScript function that caches the token per user and refreshes it shortly before expiry. Uses fetch to POST to the token endpoint. Throws an error if the request fails. Do not request a token on every API call to avoid overloading SendPulse.
```js
const cache = new Map(); // userId -> { token, exp }
async function getToken(userId) {
const c = cache.get(userId);
if (c && c.exp > Date.now() + 60_000) return c.token;
const { client_id, client_secret } = await loadUserKeys(userId);
const r = await fetch('https://api.sendpulse.com/oauth/access_token/market', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ client_id, client_secret, grant_type: 'client_credentials' }),
});
if (!r.ok) throw new Error(`Token request failed: ${r.status}`);
const j = await r.json();
cache.set(userId, { token: j.access_token, exp: Date.now() + j.expires_in * 1000 });
return j.access_token;
}
```
--------------------------------
### Cache token in Python with requests
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/07-access-token.md
Python function that caches the token per user and refreshes it shortly before expiry. Uses requests.post with a timeout of 10 seconds. Raises an exception on HTTP errors. Do not request a token on every API call to avoid overloading SendPulse.
```python
_cache = {}
def get_token(user_id):
c = _cache.get(user_id)
if c and c["exp"] > time.time() + 60:
return c["token"]
keys = load_user_keys(user_id)
r = requests.post(
"https://api.sendpulse.com/oauth/access_token/market",
json={**keys, "grant_type": "client_credentials"},
timeout=10,
)
r.raise_for_status()
j = r.json()
_cache[user_id] = {"token": j["access_token"], "exp": time.time() + j["expires_in"]}
return j["access_token"]
```
--------------------------------
### Uninstall webhook request from SendPulse (HTTP and JSON payload)
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/08-uninstall-webhook.md
SendPulse sends a POST request to your Uninstall URL with a JSON body containing connection data. The payload includes id, user_id, app_id, client_id, client_secret, and timestamps. The connection is removed regardless of your response.
```http
POST https://YOUR_UNINSTALL_URL
Content-Type: application/json
```
```json
{
"id": "9b0f56dd-4f4d-4792-ae20-23f99659b69e",
"user_id": 7043663,
"app_id": "9b0fdd98-d75f-4562-887e-2b79bc8a1eee",
"client_id": "b2bfd8a8441294fa0171c195ada8cf56",
"client_secret": "f7064fb0bbecd392d7b3ea2e2bd77758",
"created_at": "2024-01-10T15:50:20.000000Z",
"updated_at": "2024-01-10T15:50:20.000000Z"
}
```
--------------------------------
### Request app user token with POST /oauth/access_token/market
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/07-access-token.md
HTTP request to obtain an app user token. Requires client_id and client_secret from the authorization flow, and grant_type must be client_credentials. Use this endpoint only for app user keys, not for a user's own account keys.
```http
POST https://api.sendpulse.com/oauth/access_token/market
Content-Type: application/json
```
```json
{
"client_id": "b2bfd8a8441294fa0171c195ada8cf56",
"client_secret": "f7064fb0bbecd392d7b3ea2e2bd77758",
"grant_type": "client_credentials"
}
```
--------------------------------
### POST /custom-telephony/webhooks
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/13-telephony-starter-kit.md
Maps your provider's events to the provided parameters. The exact set depends on the provider. Missed calls create a task in CRM, and call end closes the call pop-up.
```APIDOC
## POST /custom-telephony/webhooks
### Description
Map your provider's events to these parameters (exact set depends on the provider). Missed call → CRM creates a task. Call end → the call pop-up closes.
### Method
POST
### Endpoint
/custom-telephony/webhooks
### Parameters
#### Request Body
- **call_id** (string) - Required - Unique call ID.
- **event** (string) - Required - Call start or call end.
- **type** (string) - Required - Incoming or outgoing.
- **phone** (string) - Required - International format.
- **duration** (number) - Required - Call duration.
- **is_success** (boolean) - Required - Whether the call connected.
- **token** (string) - Required - Webhook authorization token.
### Response
No response details provided in the source.
```
--------------------------------
### Exchange code for app user keys
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/06-authorization-flow.md
Send a POST request to the SendPulse OAuth endpoint with your app_id, secret, and the code from step 1 to obtain app user keys. The request must use JSON content type.
```http
POST https://api.sendpulse.com/market-service/oauth/authorize
Content-Type: application/json
```
```json
{
"app_id": "9b0f2f98-d75f-4562-887e-2b79bc8a1eee",
"secret": "0d028163-9c84-40e4-8508-f4927badf735",
"code": "21efd6b100b2602cb2d75cb06c2bf37f"
}
```
--------------------------------
### Error response for invalid or expired code
Source: https://github.com/ymaievska-lang/sendpulse-dev-docs-and-marketplace/blob/main/docs/06-authorization-flow.md
When the code is expired, already used, or app_id/secret are wrong, the API returns a 422 error. Branch on result === false and the numeric code, not on the error string.
```json
{
"result": false,
"data": null,
"error": "Сonnect not found!",
"code": 5
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.