This page is for developers building a plugin or an application that connects to a customer's NewsMAN account. Instead of asking the customer to find and paste an API key, you send them to NewsMAN, they approve your application, and you receive an API key for their account.
The process is three steps: send the user to the authorization page, receive an authorization code, exchange that code for an API key. Three endpoints are involved, all on this instance:
https://newsman.app/admin/oauth/authorize - where you send the customerhttps://newsman.app/admin/oauth/token - where you exchange the code for a keyhttps://newsman.app/admin/oauth/revoke - where you give a key backContact NewsMAN before you build anything. Your application is registered by hand, and you are given a client_id and a client_secret that belong to it alone. There is no self-service registration, and neither value is something you can produce yourself: a NewsMAN API key is not a client secret and cannot be used as one.
Tell us, at the same time:
If you also want to offer quick account creation - the option that lets a customer who does not have a NewsMAN account yet create one during the authorization step, with their details already filled in - ask for it as well. It needs a signature computed from your client_id and the customer's details, and both the secret it uses and the method for computing it are given to you directly. Neither is published here.
Your redirect_uri is where NewsMAN sends the customer once they have approved, and it is settled when your application is registered. There are two ways to register it, and which one applies to you depends on where your application runs.
If your application runs in one place - your own service, one address - give us that address. It is then matched exactly: the redirect_uri you send on every authorization request must be that string, character for character. A second address can be registered alongside it if you need one, for a staging environment or a second product.
If your application is a plugin that customers install on their own sites, each installation has its own callback and we cannot list them in advance. Say so when you register, and your application is allowed to supply a different callback URL each time. In that case the approval screen also shows the customer the exact address the code will be sent to - the whole host, so shop.example.com and not example.com - so tell them what to expect.
Whichever kind you register, a callback must be an absolute http or https URL with a domain name. An address written as an IP is refused, as is a URL carrying credentials before the host, and one containing a backslash, a space or a control character: a browser and our own parser do not read those the same way, so we cannot promise the customer is told where they are really going. For an internationalized domain, register the ASCII form - xn--mller-kva.example rather than müller.example - because that is the form we can show the customer unchanged.
Either way, the callback you use to start the flow is the one you must send again when you exchange the code, and it is checked then. Your callback may carry a query string of its own and we keep it, but it must not already contain code, state, error or error_description: those are the names we add, and a name that arrives twice is read as the first value by some libraries and the last by others. If your callback URL ends in a fragment - anything from a # onwards - the response parameters are added before it, so read code and state from the query string and not from the fragment.
Redirect the customer's browser to https://newsman.app/admin/oauth/authorize with these query parameters:
response_type - always code, the only response type supported.client_id - the id issued to your application.scope - always api. It is the only scope that exists, it is not a permission selector, and it does not narrow what the resulting key can do.redirect_uri - your callback URL, URL-encoded.state - optional but recommended: any value you generate, returned to you unchanged. Use it to tie the response back to the request and to protect against cross-site request forgery.The complete URL:
https://newsman.app/admin/oauth/authorize?response_type=code&client_id=yourplugin&scope=api&redirect_uri=https%3A%2F%2Fyourplugin.example%2Fnewsman%2Fcallback&state=6f1a9c2eIf the customer is not signed in, NewsMAN asks them to sign in first and then returns them to the approval screen automatically. The screen shows them your application's name, and the site being authorized when your callbacks are per-installation.
Failures in the request itself do not reach your callback at all. If the client_id is unknown, the redirect_uri does not match your registration, or response_type or scope is not the value above, the customer is shown an HTTP 400 with a JSON body instead of being redirected - there is no verified address to send them to. Seeing that in a browser means your authorization URL and your registration disagree.
When the customer approves, NewsMAN redirects the browser back to your redirect_uri with the authorization code appended, along with your state value if you sent one:
https://yourplugin.example/newsman/callback?code=01****************************84&state=6f1a9c2eWhen the customer does not approve, or cannot, you are sent back to the same URL with an error instead of a code. There are three, and you should handle all of them - the first is a normal outcome, not a failure:
error=access_denied&error_description=end-user+denied+authorization - the customer pressed the decline button.error=access_denied&error_description=end-user+denied+authorization+for+list - the customer did not choose a list on a screen that required one.error=missing_lists&error_description=no-lists+found+authorization - the account has no lists at all, so there is nothing to grant access to. Tell the customer to create a list in NewsMAN and start again.Your state value comes back on these redirects too, so the check you run on a successful callback applies to a denial as well.
Send a POST request to https://newsman.app/admin/oauth/token. The parameters are form-encoded:
grant_type - always authorization_code.code - the code you received in step 2.client_id - the id issued to your application.client_secret - the secret issued with it. Send it from your server, never from the customer's browser.redirect_uri - the same value you sent in step 1.curl -X POST 'https://newsman.app/admin/oauth/token' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=01****************************84' \
--data-urlencode 'client_id=yourplugin' \
--data-urlencode 'client_secret=7c****************************e9' \
--data-urlencode 'redirect_uri=https://yourplugin.example/newsman/callback'A successful exchange returns HTTP 200 and a JSON body:
{
"access_token": "9f****************************c4",
"token_type": "bearer",
"scope": "api",
"expires_in": 1804204800,
"user_id": 58000032,
"username": "client@example.com",
"firstname": "John",
"lastname": "Doe",
"lists": [5130, 5131],
"api_endpoint": "https://api.newsman.app/v2/",
"lists_data": {
"5130": {"list_id": "5130", "name": "Newsletter", "type": "newsletter"},
"5131": {"list_id": "5131", "name": "SMS alerts", "type": "sms"}
}
}The fields that matter are access_token, user_id, lists and api_endpoint. They are described in What you now have. expires_in is not one of them - see How long the key lasts before you build anything on it.
If the exchange fails you get HTTP 400 and a JSON body naming the error:
{
"error": "invalid_grant",
"error_description": "Authorization code doesn't exist or is invalid for the client"
}The error values are:
invalid_request - grant_type was not sent.unsupported_grant_type - grant_type was something other than authorization_code.invalid_grant - the code is unknown.invalid_client - the client_secret is missing or wrong, or the code was issued to a different client_id.redirect_uri_mismatch - the redirect_uri is missing, or is not the one the code was issued for.access_token is the customer's NewsMAN API key, and user_id is the account it belongs to. Together they authenticate every subsequent API call, so store both.
api_endpoint is the API base URL for that account. It is derived from the account rather than from the host you just called, so it can differ from the host in your authorization URL. Store the value you were given instead of assuming one.
How to authenticate a call with the key, and what methods exist, is documented in the REST API reference, which you can also reach from the switch at the top of this page.
lists holds the ids of the lists your key may work with: every list the person who approved your application has access to. A call naming any other list is rejected. If they approved from a sub-account, that is their own set of lists rather than every list on the account.
lists_data describes them - the id, the name the customer gave the list, and its type, which is newsletter for an email list or sms for an SMS list. Use it to show the customer which of their lists you are connected to, without a further API call.
lists_data is only present when the grant covers at least one list, so read it defensively. lists is the field to rely on.
The set is fixed when the customer approves. A list they create afterwards is not added to an existing key by itself: the customer can add it by editing the key under Account and then API, or you can send them through the flow again for a fresh key. The same screen lets them restrict the key - to certain lists, to certain IP addresses, or to import and subscribe only - so a call can start failing without the key having been revoked.
The key does not expire. It stays valid until it is revoked.
Ignore expires_in. It is in the response for compatibility, but nothing enforces it and its value is not a duration, so an expiry timer built on it will be wrong.
There is no refresh token and no refresh step, because there is nothing to refresh. If a key is no longer accepted at all, it was revoked, and the way to get a new one is to run the authorization flow again.
That is the failure mode to design for. A key does not lapse at a time you can predict and prepare for - it stops the moment a customer disconnects your application, and you find out on the next call you make. Treat an authentication failure as "reconnect this account" rather than as an error to retry.
Either side can revoke:
https://newsman.app/admin/oauth/revoke as the token parameter. Do this when a customer uninstalls your plugin, so you do not leave a working key behind.