=============== 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.

New customers will be added to this list.

DateContacts syncedStatus
2026-10-14120OK
``` -------------------------------- ### 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.