Integrate a SaaS platform on Stripe or Adyen

Connect Stripe or Adyen once and Coris runs its checks from your processor events. On Stripe, add payment context with POST /transaction_metadata/ if you need it.

Connect Stripe in the portal, or have Coris connect Adyen, and processor events then drive Coris. Many platforms make no API call, and yours never calls Merchant Profiler or CorShield directly. On Stripe, add payment context with POST /transaction_metadata/.

Stripe or AdyenCorisYour platform
  1. Your platform → Coris
    1Connect your processor
  2. Stripe or Adyen → Coris
    2Connected account event
  3. Coris
    3Match the business and create the account
  4. Coris
    4Run your onboarding monitoring
  5. Coris
    5Run Merchant Profiler and CorShield data as your rules require
  6. Coris → Your platform
    6Webhook:alert.created, account.tag.created
  7. Stripe or Adyen → Coris
    7Payment, payout and dispute events
  8. Coris
    8Run your monitoring
  9. Coris → Your platform
    9Webhook and portal
  10. Your platform → Coris
    10Stripe only:POST /transaction_metadata/

Data you send and receive

SourceData
Processor to CorisConnected accounts, persons, payments, payouts, disputes
You to CorisTransaction metadata, plus optional tags and lists
Coris to youWebhook events and portal views

Setup checklist

Connect your processor first.

For API calls only:

Events to enable

Processor events run your agreed monitoring, including Merchant Profiler data and CorShield when your rules use them. With no rules, no monitoring runs.

On Stripe you enable nothing, because Coris creates its webhook endpoints when you connect. For a security review, Stripe access covers the events and key access. To learn how charge types affect monitoring, ask Coris.

On Adyen, send these events to the webhook URL Coris gives you:

  • balancePlatform.accountHolder.created, balancePlatform.accountHolder.updated
  • balancePlatform.balanceAccount.created, balancePlatform.balanceAccount.updated
  • balancePlatform.transfer.created, balancePlatform.transfer.updated
  • AUTHORISATION, CAPTURE
  • CHARGEBACK, REQUEST_FOR_INFORMATION, NOTIFICATION_OF_CHARGEBACK, SECOND_CHARGEBACK, CHARGEBACK_REVERSED, NOTIFICATION_OF_FRAUD

Endpoints you use

JobEndpoint
Add payment contextUpdate transaction metadata
Receive resultsCreate a webhook endpoint
Optional: add account dataUpdate account metadata
Optional: label a merchantSet an account tag

Add payment context

Step 1: Set your base URL and key

Every sample below uses these values.

export CORIS_BASE_URL="https://sandbox-api.coris.ai/api/v0_2"
export CORIS_API_KEY="YOUR_API_KEY"
import time
import requests

BASE_URL = "https://sandbox-api.coris.ai/api/v0_2"
HEADERS = {"Authorization": "Token YOUR_API_KEY"}
const BASE_URL = "https://sandbox-api.coris.ai/api/v0_2";
const HEADERS = {
  Authorization: "Token YOUR_API_KEY",
  "Content-Type": "application/json",
};

Step 2: Add metadata to a Stripe payment

Attach your own fields to one Stripe payment, by ch_ ID with stripe_charge or pi_ ID with stripe_payment_intent. Read back with the same type, because a charge and its payment intent do not share metadata.

Until the payment reaches Coris, GET returns 404 and Coris holds what you post. You cannot list or cancel held metadata. Coris applies it in order on arrival, so a later post with null removes that key.

curl -s -o /dev/null -w '%{http_code}\n' -X POST \
  "$CORIS_BASE_URL/transaction_metadata/?source_transaction_type=stripe_charge&source_transaction_id=ch_1AcmeExample" \
  -H "Authorization: Token $CORIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "job_type": "water_heater_install" }'
response = requests.post(
    f"{BASE_URL}/transaction_metadata/",
    params={
        "source_transaction_type": "stripe_charge",
        "source_transaction_id": "ch_1AcmeExample",
    },
    headers=HEADERS,
    json={"job_type": "water_heater_install"},
)
print(response.status_code)
const query = new URLSearchParams({
  source_transaction_type: "stripe_charge",
  source_transaction_id: "ch_1AcmeExample",
});
const txnMetadata = await fetch(`${BASE_URL}/transaction_metadata/?${query}`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({ job_type: "water_heater_install" }),
});
console.log(txnMetadata.status);

Try it.

Step 3 (optional): Add metadata to an account

Store data about a merchant on its account, with your Stripe account ID in the path. The call returns 404 until that account exists in Coris. Set a value to null to remove a key.

curl -s -X POST "$CORIS_BASE_URL/accounts/acct_1AcmeExample/metadata/" \
  -H "Authorization: Token $CORIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "plan": "pro", "signup_channel": "partner" }'
response = requests.post(
    f"{BASE_URL}/accounts/acct_1AcmeExample/metadata/",
    headers=HEADERS,
    json={"plan": "pro", "signup_channel": "partner"},
)
print(response.status_code, response.text)
const metadata = await fetch(`${BASE_URL}/accounts/acct_1AcmeExample/metadata/`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({ plan: "pro", signup_channel: "partner" }),
});
console.log(metadata.status, await metadata.text());

Try it.

Step 4 (optional): Keep a custom list

Keep a list of IDs or terms in Coris. list_type tells Coris how to use the list.

curl -s -X POST "$CORIS_BASE_URL/settings/custom_list/" \
  -H "Authorization: Token $CORIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"Trusted accounts $(date +%s)\",
    \"description\": \"Accounts your team has reviewed\",
    \"list_type\": \"account_ids\",
    \"items\": [\"acct_1AcmeExample\"]
  }" | jq
response = requests.post(
    f"{BASE_URL}/settings/custom_list/",
    headers=HEADERS,
    json={
        "name": f"Trusted accounts {int(time.time())}",
        "description": "Accounts your team has reviewed",
        "list_type": "account_ids",
        "items": ["acct_1AcmeExample"],
    },
)
print(response.status_code, response.json())
const list = await fetch(`${BASE_URL}/settings/custom_list/`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    name: `Trusted accounts ${Date.now()}`,
    description: "Accounts your team has reviewed",
    list_type: "account_ids",
    items: ["acct_1AcmeExample"],
  }),
});
console.log(list.status, await list.json());

Try it.

Read the response

The custom list call returns the list you created. Step 2 returns 202 with an empty body.

{
  "id": "d29a7c14-3b6e-4f08-95c1-7e2b0a6d8f33",
  "name": "Trusted accounts 1762300800",
  "description": "Accounts your team has reviewed",
  "list_type": "account_ids",
  "items": ["acct_1AcmeExample"]
}
FieldWhat to do
idUse it to update the list.
list_typePick the type that matches your items.
itemsSend the full list on update.

What to do next

EventYour action
alert.createdReview it in the portal. See Monitor your portfolio.
transaction.alert.createdRoute it. See Triage transaction alerts.
A Stripe disputeWork it in the portal. See Respond to disputes.

For your risk, compliance and security reviewers: How Coris checks work, Monitoring and alerts explained and Security and compliance.

FAQ

What must a Stripe account have before Coris onboards it?

It needs accepted terms of service and enough business details for a search. Until then, Coris waits for a later event. An account can exist in Coris before this, and onboarded turns true once onboarding has run.

Are new accounts monitored?

Yes, by default. See Monitor your portfolio.

Can I add metadata to an Adyen payment?

No, only Stripe payments. On Adyen, use Update account metadata.

Do tags I send trigger webhooks?

Yes. Each change sends an account.tag.* event to subscribed endpoints. Filter out changes your own system made.

Next: Connect Stripe and Adyen


Did this page help you?