coss 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ //#region src/device.d.ts
2
+ export interface CollectDeviceOptions {
3
+ /**
4
+ * Where the device id is kept: `local` (default) remembers it in `localStorage`; `memory`
5
+ * keeps it only for this page (for example until the user consents to storage).
6
+ */
7
+ storage?: "local" | "memory";
8
+ }
9
+ /** Returns a token for `device.token` in `coss.risk.assess()`. Make a new one per submission. */
10
+ export declare function collectDevice(options?: CollectDeviceOptions): string;
11
+ //#endregion
package/dist/device.js ADDED
@@ -0,0 +1,71 @@
1
+ //#region src/device.ts
2
+ /**
3
+ * `coss/device`: recognizes the browser behind risk events. Call `collectDevice()` in the browser
4
+ * when the user submits a form, send the token to your server with it, and pass it to
5
+ * `coss.risk.assess({ device: { token } })`.
6
+ *
7
+ * The token carries a random id this browser keeps in `localStorage` (`coss_device_id`, no
8
+ * cookies) and coarse facts: time zone, languages, platform, screen size, touch support, and
9
+ * whether the browser is automated (WebDriver, headless). No canvas, WebGL, font or audio
10
+ * fingerprinting: a browser whose storage is cleared looks like a new one. COSS keeps only a keyed
11
+ * hash of the id.
12
+ *
13
+ * Collects nothing until called, and never throws: outside a browser the token simply carries
14
+ * fewer facts.
15
+ */
16
+ /** The token format version, checked by the API. */
17
+ const TOKEN_PREFIX = "cd1.";
18
+ const STORAGE_KEY = "coss_device_id";
19
+ const HEADLESS = /HeadlessChrome|PhantomJS|SlimerJS/;
20
+ let memoryId;
21
+ function randomId() {
22
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
23
+ globalThis.crypto.getRandomValues(bytes);
24
+ return base64Url(bytes);
25
+ }
26
+ function base64Url(bytes) {
27
+ let binary = "";
28
+ for (const byte of bytes) binary += String.fromCharCode(byte);
29
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
30
+ }
31
+ function deviceId(storage) {
32
+ if (storage === "local") try {
33
+ const store = globalThis.localStorage;
34
+ if (store) {
35
+ const stored = store.getItem(STORAGE_KEY);
36
+ if (stored && /^[A-Za-z0-9_-]{16,64}$/.test(stored)) return stored;
37
+ const id = randomId();
38
+ store.setItem(STORAGE_KEY, id);
39
+ return id;
40
+ }
41
+ } catch {}
42
+ memoryId ??= randomId();
43
+ return memoryId;
44
+ }
45
+ function safely(read, fallback) {
46
+ try {
47
+ return read() ?? fallback;
48
+ } catch {
49
+ return fallback;
50
+ }
51
+ }
52
+ /** Returns a token for `device.token` in `coss.risk.assess()`. Make a new one per submission. */
53
+ function collectDevice(options = {}) {
54
+ const nav = globalThis.navigator;
55
+ const screen = globalThis.screen;
56
+ const userAgent = safely(() => nav?.userAgent ?? "", "");
57
+ const payload = {
58
+ id: deviceId(options.storage ?? "local"),
59
+ t: Date.now(),
60
+ tz: safely(() => Intl.DateTimeFormat().resolvedOptions().timeZone.slice(0, 64) || null, null),
61
+ l: safely(() => [...nav?.languages ?? []].slice(0, 10).map((l) => l.slice(0, 35)), []),
62
+ p: safely(() => (nav?.userAgentData?.platform || nav?.platform || "").slice(0, 64) || null, null),
63
+ s: safely(() => screen ? [Math.round(screen.width), Math.round(screen.height)] : null, null),
64
+ tp: safely(() => typeof nav?.maxTouchPoints === "number" ? Math.round(nav.maxTouchPoints) : null, null),
65
+ wd: safely(() => nav?.webdriver === true, false),
66
+ hl: HEADLESS.test(userAgent)
67
+ };
68
+ return `${TOKEN_PREFIX}${base64Url(new TextEncoder().encode(JSON.stringify(payload)))}`;
69
+ }
70
+ //#endregion
71
+ export { collectDevice };
package/dist/index.d.ts CHANGED
@@ -437,6 +437,15 @@ interface EmailLink {
437
437
  first_clicked_at: string | null;
438
438
  last_clicked_at: string | null;
439
439
  }
440
+ /** Why an email failed as a whole. */
441
+ interface EmailFailure {
442
+ /**
443
+ * E.g. `all_recipients_suppressed`, `domain_not_verified`, `content_rejected`,
444
+ * `attachments_unavailable`, `dispatch_failed` or `provider_error`.
445
+ */
446
+ code: string;
447
+ message: string;
448
+ }
440
449
  /** An email as returned by the API. Recipient fields are always arrays. */
441
450
  interface Email {
442
451
  object: "email";
@@ -454,6 +463,8 @@ interface Email {
454
463
  tags: EmailTag[];
455
464
  category: string | null;
456
465
  status: EmailStatus;
466
+ /** Why the email failed; `null` unless `status` is `"failed"`. */
467
+ failure: EmailFailure | null;
457
468
  /** Every distinct recipient across `to`, `cc` and `bcc`, with its delivery status. */
458
469
  recipients: EmailRecipient[];
459
470
  /** Attachment metadata; the content is discarded once the email is sent. */
@@ -470,6 +481,31 @@ interface Email {
470
481
  /** The batch that sent the email (`coss.email.sendBatch`), or `null` if it was sent alone. */
471
482
  batch_id: string | null;
472
483
  }
484
+ /** An email in a list (`coss.email.list`): everything but its content. */
485
+ type EmailSummary = Omit<Email, "html" | "text" | "headers" | "attachments">;
486
+ /** Filters of `coss.email.list`. Emails are returned newest first. */
487
+ interface ListEmailsParams {
488
+ /** Only emails with this recipient (in `to`, `cc` or `bcc`; the bare address, any case). */
489
+ to?: string;
490
+ /** Only emails with a recipient in one of these statuses (with `to`, that recipient's). */
491
+ recipient_status?: EmailRecipientStatus[];
492
+ /** Only emails in these statuses. */
493
+ status?: EmailStatus[];
494
+ /** Free text, matched against the subject and the `to` addresses. */
495
+ q?: string;
496
+ /** Only emails of this category. */
497
+ category?: string;
498
+ /** Only the emails of this batch. */
499
+ batch_id?: string;
500
+ /** Only emails created at or after this time. */
501
+ created_after?: Date | string;
502
+ /** Only emails created before this time. */
503
+ created_before?: Date | string;
504
+ /** 1-100, default 20. */
505
+ limit?: number;
506
+ /** Cursor: the `id` of the last email of the previous page. */
507
+ after?: string;
508
+ }
473
509
  /** Emails sent together by `coss.email.sendBatch`. */
474
510
  interface EmailBatch {
475
511
  object: "email_batch";
@@ -1199,6 +1235,12 @@ declare class EmailResource {
1199
1235
  sendBatch(emails: SendEmailParams[], options?: RequestOptions): Promise<CossResult<EmailBatch>>;
1200
1236
  /** Retrieves an email sent from the same environment as the API key. */
1201
1237
  get(id: string, options?: Pick<RequestOptions, "signal">): Promise<CossResult<Email>>;
1238
+ /**
1239
+ * Lists emails sent from the same environment as the API key, newest first and without their
1240
+ * content (`get` one for `html`, `text`, `headers` and `attachments`). Pass the last `id` as
1241
+ * `after` while `has_more` is true.
1242
+ */
1243
+ list(params?: ListEmailsParams, options?: Pick<RequestOptions, "signal">): Promise<CossResult<List<EmailSummary>>>;
1202
1244
  /**
1203
1245
  * Cancels a scheduled email. Canceling an already canceled email returns it; any other email
1204
1246
  * fails with `email_not_cancelable`.
@@ -1896,9 +1938,16 @@ type RiskList = "allow" | "block";
1896
1938
  */
1897
1939
  type RiskListEntryType = "email" | "domain" | "ip" | "asn" | "country" | "user";
1898
1940
  /** Signal groups an assessment was made without (a lookup failed or timed out). */
1899
- type RiskDegradedSignal = "email_dns" | "domain_age" | "ip_data" | "velocity" | "captcha";
1941
+ type RiskDegradedSignal = "email_dns" | "domain_age" | "ip_data" | "velocity" | "captcha" | "content" | "ip_privacy";
1942
+ /** What content classification scores. */
1943
+ type RiskContentCategory = "spam" | "phishing" | "malware" | "fraud";
1944
+ /**
1945
+ * Per category, the model's probability (0-1) that the content belongs to it. Not calibrated:
1946
+ * pick thresholds by looking at your own traffic.
1947
+ */
1948
+ type RiskContentScores = Record<RiskContentCategory, number>;
1900
1949
  /** Every reason an assessment can give (see the reason catalog in the docs). */
1901
- type RiskReasonCode = "email.invalid" | "email.disposable" | "email.undeliverable" | "email.role_account" | "email.typo" | "email.domain_new" | "ip.tor" | "ip.vpn_network" | "ip.hosting_provider" | "ip.private" | "velocity.signups_per_ip_1h" | "velocity.signups_per_ip_24h" | "velocity.signups_per_subnet_1h" | "velocity.signups_per_email_domain_1h" | "velocity.signups_per_mailbox_24h" | "velocity.logins_per_ip_1h" | "velocity.logins_per_user_1h" | "velocity.events_per_ip_1h" | "velocity.events_per_user_1h" | "duplicate.canonical_mailbox" | "history.fraud_mailbox" | "history.fraud_user" | "history.fraud_ip" | "history.fraud_subnet" | "login.new_country" | "login.new_network" | "captcha.failed" | "captcha.missing" | "rule.matched" | "list.blocked" | "list.allowed";
1950
+ type RiskReasonCode = "email.invalid" | "email.disposable" | "email.undeliverable" | "email.role_account" | "email.typo" | "email.domain_new" | "ip.tor" | "ip.vpn_network" | "ip.hosting_provider" | "ip.vpn" | "ip.proxy" | "ip.relay" | "ip.private" | "velocity.signups_per_ip_1h" | "velocity.signups_per_ip_24h" | "velocity.signups_per_subnet_1h" | "velocity.signups_per_email_domain_1h" | "velocity.signups_per_mailbox_24h" | "velocity.signups_per_device_24h" | "velocity.checkouts_per_ip_1h" | "velocity.checkouts_per_user_1h" | "velocity.checkouts_per_card_24h" | "velocity.logins_per_ip_1h" | "velocity.logins_per_user_1h" | "velocity.events_per_ip_1h" | "velocity.events_per_user_1h" | "duplicate.canonical_mailbox" | "duplicate.device" | "history.fraud_mailbox" | "history.fraud_user" | "history.fraud_ip" | "history.fraud_subnet" | "history.fraud_card" | "history.fraud_device" | "checkout.card_country_mismatch" | "checkout.billing_country_mismatch" | "checkout.prepaid_card" | "device.automation" | "device.invalid" | "login.new_country" | "login.new_network" | "captcha.failed" | "captcha.missing" | "content.spam" | "content.phishing" | "content.malware" | "content.fraud" | "rule.matched" | "list.blocked" | "list.allowed";
1902
1951
  /** Why an assessment decided what it did. Never show reasons to the person being assessed. */
1903
1952
  interface RiskReason {
1904
1953
  code: RiskReasonCode;
@@ -1911,11 +1960,11 @@ interface RiskReason {
1911
1960
  window?: "1h" | "24h";
1912
1961
  /** `email.typo`: the likely domain, which you may show the user ("Did you mean …?"). */
1913
1962
  suggestion?: string;
1914
- /** `ip.hosting_provider`, `ip.vpn_network`: the network's operator. */
1963
+ /** `ip.hosting_provider`, `ip.vpn_network`: the network's operator; `ip.vpn`: the VPN service. */
1915
1964
  provider?: string;
1916
- /** `duplicate.canonical_mailbox`: your id of the user who has the mailbox, if known. */
1965
+ /** `duplicate.*`: your id of the user who has the mailbox or device, if known. */
1917
1966
  previous_user?: string | null;
1918
- /** `duplicate.canonical_mailbox`: the assessment that first saw the mailbox. */
1967
+ /** `duplicate.*`: the assessment that first saw the mailbox or device. */
1919
1968
  previous_assessment?: string;
1920
1969
  /** `list.*`: the matching list entry. */
1921
1970
  entry?: string;
@@ -1963,6 +2012,45 @@ interface RiskIpSignals {
1963
2012
  as_name: string | null;
1964
2013
  /** ISO 3166-1 alpha-2. */
1965
2014
  country: string | null;
2015
+ /**
2016
+ * From COSS's IP privacy data provider; `null` when it isn't available or was not asked
2017
+ * (private addresses).
2018
+ */
2019
+ privacy: RiskIpPrivacy | null;
2020
+ }
2021
+ /** Whether an address is a VPN exit, a proxy or a privacy relay (iCloud Private Relay…). */
2022
+ interface RiskIpPrivacy {
2023
+ vpn: boolean;
2024
+ proxy: boolean;
2025
+ relay: boolean;
2026
+ /** The VPN or proxy service. */
2027
+ service: string | null;
2028
+ }
2029
+ /** How a card is funded, as your payment processor reports it. */
2030
+ type RiskCardFunding = "credit" | "debit" | "prepaid" | "unknown";
2031
+ /** What you sent in `checkout` (never the card). */
2032
+ interface RiskCheckoutSignals {
2033
+ amount: number;
2034
+ currency: string;
2035
+ card_country: string | null;
2036
+ card_funding: RiskCardFunding | null;
2037
+ billing_country: string | null;
2038
+ shipping_country: string | null;
2039
+ }
2040
+ /** What the browser reported through `coss/device` (its device id is only kept as a keyed hash). */
2041
+ interface RiskDeviceSignals {
2042
+ /** `false`: the token was malformed or too old. */
2043
+ valid: boolean;
2044
+ /** WebDriver control or a headless browser. */
2045
+ automation: boolean;
2046
+ /** IANA time zone (`Europe/London`). */
2047
+ timezone: string | null;
2048
+ /** The browser's first language. */
2049
+ language: string | null;
2050
+ platform: string | null;
2051
+ /** `<width>x<height>` in CSS pixels. */
2052
+ screen: string | null;
2053
+ touch: boolean | null;
1966
2054
  }
1967
2055
  /** An assessment of one event. */
1968
2056
  interface RiskAssessment {
@@ -1985,6 +2073,10 @@ interface RiskAssessment {
1985
2073
  velocity: Record<string, number> | null;
1986
2074
  captcha: RiskCaptchaSignals | null;
1987
2075
  login: RiskLoginSignals | null;
2076
+ /** `null` when no content was given or it couldn't be classified. */
2077
+ content: RiskContentScores | null;
2078
+ checkout: RiskCheckoutSignals | null;
2079
+ device: RiskDeviceSignals | null;
1988
2080
  };
1989
2081
  user: {
1990
2082
  id: string | null;
@@ -2022,6 +2114,38 @@ interface AssessRiskParams {
2022
2114
  captcha?: {
2023
2115
  token: string;
2024
2116
  };
2117
+ /**
2118
+ * Text the user wrote (a bio, a message, a listing), classified with the event and never
2119
+ * stored: its scores go in `signals.content`, and `content.*` reasons fire only at thresholds
2120
+ * you set.
2121
+ */
2122
+ content?: {
2123
+ text: string;
2124
+ };
2125
+ /** Only with `checkout` events: what is being paid for, and with which card. */
2126
+ checkout?: {
2127
+ /** In the currency's minor unit (cents for USD: 1999 is $19.99). */
2128
+ amount: number;
2129
+ /** ISO 4217, upper-case (`USD`). */
2130
+ currency: string;
2131
+ /** What your payment processor tells you about the card. */
2132
+ card?: {
2133
+ /**
2134
+ * Your processor's fingerprint of the card number (Stripe: `card.fingerprint`), kept as a
2135
+ * keyed hash only. Never send the card number.
2136
+ */
2137
+ fingerprint?: string;
2138
+ /** Where the card was issued (ISO 3166-1 alpha-2, upper-case). */
2139
+ country?: string;
2140
+ funding?: RiskCardFunding;
2141
+ };
2142
+ billing_country?: string;
2143
+ shipping_country?: string;
2144
+ };
2145
+ /** What `collectDevice()` from `coss/device` returned in the user's browser. */
2146
+ device?: {
2147
+ token: string;
2148
+ };
2025
2149
  }
2026
2150
  /** Query of `GET /v1/risk/assessments`. */
2027
2151
  interface ListRiskAssessmentsParams {
@@ -2114,6 +2238,10 @@ interface RiskSettings {
2114
2238
  provider: RiskCaptchaProvider;
2115
2239
  required: boolean;
2116
2240
  } | null;
2241
+ /** Per content category, the score at which `content.<category>` fires; `null`: never. */
2242
+ content_thresholds: Record<RiskContentCategory, number | null>;
2243
+ /** Reasons whose weight this environment changed; the rest weigh what the catalog says. */
2244
+ reason_weights: Partial<Record<RiskReasonCode, number>>;
2117
2245
  /** ISO 8601 timestamp; `null` while on the defaults. */
2118
2246
  updated_at: string | null;
2119
2247
  }
@@ -2130,9 +2258,48 @@ interface UpdateRiskSettingsParams {
2130
2258
  /** Assessments without a token get `captcha.missing`. */
2131
2259
  required?: boolean;
2132
2260
  } | null;
2261
+ /** Changes the categories given: a score in (0, 1] sets the threshold, `null` removes it. */
2262
+ content_thresholds?: Partial<Record<RiskContentCategory, number | null>>;
2263
+ /**
2264
+ * Changes the reasons given: a weight in [0, 0.99] replaces the default (for example
2265
+ * calibration's `suggested_weight`), `null` restores it. Not `list.*` or `rule.matched`.
2266
+ */
2267
+ reason_weights?: Partial<Record<RiskReasonCode, number | null>>;
2268
+ }
2269
+ /** Feedback counted against one reason or rule (`fraud` includes `abuse`). */
2270
+ interface RiskCalibrationCounts {
2271
+ fraud: number;
2272
+ legitimate: number;
2273
+ /** `fraud / (fraud + legitimate)`; `null` without feedback. */
2274
+ precision: number | null;
2275
+ }
2276
+ /** How often each reason and rule was right over the last 90 days, by your feedback. */
2277
+ interface RiskCalibration {
2278
+ object: "risk_calibration";
2279
+ window_days: number;
2280
+ /** Assessments in the window. */
2281
+ assessments: number;
2282
+ /** Of those, with feedback. */
2283
+ labeled: number;
2284
+ reasons: (RiskCalibrationCounts & {
2285
+ code: RiskReasonCode;
2286
+ /** Assessments it fired in. */
2287
+ fired: number;
2288
+ default_weight: number;
2289
+ /** Its weight in this environment now. */
2290
+ weight: number;
2291
+ /** What feedback supports; `null` with fewer than 20 assessments with feedback. */
2292
+ suggested_weight: number | null;
2293
+ })[];
2294
+ rules: (RiskCalibrationCounts & {
2295
+ rule: string;
2296
+ name: string;
2297
+ /** Assessments it decided. */
2298
+ matched: number;
2299
+ })[];
2133
2300
  }
2134
2301
  /** A field a rule condition can test (see the rules guide). */
2135
- type RiskRuleField = "email.valid" | "email.disposable" | "email.undeliverable" | "email.role_account" | "email.free_provider" | "email.typo" | "email.domain_new" | "email.domain" | "email.domain_age_days" | "ip.tor" | "ip.hosting" | "ip.vpn" | "ip.private" | "ip.country" | "ip.asn" | "ip.hosting_provider" | "captcha.passed" | "duplicate.canonical_mailbox" | "history.fraud" | "login.new_country" | "login.new_network" | "score" | "velocity.ip_1h" | "velocity.ip_24h" | "velocity.subnet_1h" | "velocity.subnet_24h" | "velocity.email_domain_1h" | "velocity.email_domain_24h" | "velocity.mailbox_1h" | "velocity.mailbox_24h" | "velocity.user_1h" | "velocity.user_24h";
2302
+ type RiskRuleField = "email.valid" | "email.disposable" | "email.undeliverable" | "email.role_account" | "email.free_provider" | "email.typo" | "email.domain_new" | "email.domain" | "email.domain_age_days" | "ip.tor" | "ip.hosting" | "ip.vpn" | "ip.proxy" | "ip.relay" | "ip.private" | "ip.country" | "ip.asn" | "ip.hosting_provider" | "captcha.passed" | "duplicate.canonical_mailbox" | "duplicate.device" | "history.fraud" | "login.new_country" | "login.new_network" | "content.spam" | "content.phishing" | "content.malware" | "content.fraud" | "checkout.amount" | "checkout.currency" | "checkout.card_country" | "checkout.card_funding" | "checkout.billing_country" | "checkout.shipping_country" | "device.automation" | "device.timezone" | "device.language" | "score" | "velocity.ip_1h" | "velocity.ip_24h" | "velocity.subnet_1h" | "velocity.subnet_24h" | "velocity.email_domain_1h" | "velocity.email_domain_24h" | "velocity.mailbox_1h" | "velocity.mailbox_24h" | "velocity.user_1h" | "velocity.user_24h" | "velocity.card_1h" | "velocity.card_24h" | "velocity.device_1h" | "velocity.device_24h";
2136
2303
  /** A test of one field. A field without a value (no IP, no email…) never matches. */
2137
2304
  interface RiskRuleCondition {
2138
2305
  field: RiskRuleField;
@@ -2179,6 +2346,107 @@ interface DeletedRiskRule {
2179
2346
  id: string;
2180
2347
  deleted: true;
2181
2348
  }
2349
+ /** Result of `coss.risk.checkEmail()`: what COSS knows about an address. Nothing is stored. */
2350
+ interface RiskEmailCheck {
2351
+ object: "risk_email_check";
2352
+ email: string;
2353
+ valid: boolean;
2354
+ domain: string | null;
2355
+ /** The mailbox with the provider's aliasing folded (dots and `+tags` at Gmail…). */
2356
+ canonical: string | null;
2357
+ disposable: boolean;
2358
+ /** From DNS (MX, A, AAAA). `unknown` when the lookup failed or timed out. */
2359
+ deliverability: "deliverable" | "undeliverable" | "unknown";
2360
+ role_account: boolean;
2361
+ free_provider: boolean;
2362
+ /** The likely intended domain when this one looks like a typo of a popular one. */
2363
+ suggestion: string | null;
2364
+ /** Days since the domain was registered (RDAP); `null` when unknown or not looked up. */
2365
+ domain_age_days: number | null;
2366
+ /** Lookups that failed or timed out. */
2367
+ degraded: ("email_dns" | "domain_age")[];
2368
+ }
2369
+ /** Result of `coss.risk.checkIp()`. Nothing is stored. */
2370
+ interface RiskIpCheck extends RiskIpSignals {
2371
+ object: "risk_ip_check";
2372
+ ip: string;
2373
+ /**
2374
+ * `ip_data` when some IP data is unavailable at the moment; `ip_privacy` when the privacy data
2375
+ * provider failed or timed out.
2376
+ */
2377
+ degraded: ("ip_data" | "ip_privacy")[];
2378
+ }
2379
+ /** Result of `coss.risk.classify()`: scores only, neither stored nor logged. */
2380
+ interface RiskContentClassification {
2381
+ object: "risk_content_classification";
2382
+ scores: RiskContentScores;
2383
+ /** The text was longer than the model reads (8,000 characters): only its beginning counted. */
2384
+ truncated: boolean;
2385
+ }
2386
+ /** What `on_failure` answers while COSS can't count. */
2387
+ type RiskLimitFailureMode = "allow" | "deny";
2388
+ /** One of your rate limit buckets: each key gets `burst` tokens, refilling `limit` per period. */
2389
+ interface RiskLimitBucket {
2390
+ object: "risk_limit_bucket";
2391
+ id: string;
2392
+ /** What `coss.risk.limit({ bucket })` names it by. */
2393
+ name: string;
2394
+ /** Tokens added per `period_seconds`. */
2395
+ limit: number;
2396
+ period_seconds: number;
2397
+ /** How many requests a key can make at once. */
2398
+ burst: number;
2399
+ on_failure: RiskLimitFailureMode;
2400
+ /** ISO 8601 timestamp. */
2401
+ created_at: string;
2402
+ /** ISO 8601 timestamp. */
2403
+ updated_at: string;
2404
+ }
2405
+ /** Body of `POST /v1/risk/limit_buckets`. */
2406
+ interface CreateRiskLimitBucketParams {
2407
+ /** 1-64 lower-case letters, digits, `.`, `_` or `-`; unique in the environment. */
2408
+ name: string;
2409
+ limit: number;
2410
+ /** 1 second to 1 day. */
2411
+ period_seconds: number;
2412
+ /** Defaults to `limit`. */
2413
+ burst?: number;
2414
+ /** Defaults to `allow`. */
2415
+ on_failure?: RiskLimitFailureMode;
2416
+ }
2417
+ /** Body of `PATCH /v1/risk/limit_buckets/{id}` (the name can't change). */
2418
+ type UpdateRiskLimitBucketParams = Partial<Omit<CreateRiskLimitBucketParams, "name">>;
2419
+ interface DeletedRiskLimitBucket {
2420
+ object: "risk_limit_bucket";
2421
+ id: string;
2422
+ deleted: true;
2423
+ }
2424
+ /** Body of `POST /v1/risk/limits`. */
2425
+ interface CheckRiskLimitParams {
2426
+ /** The bucket's name. */
2427
+ bucket: string;
2428
+ /** Who or what is limited (a user id, an email or IP address…); COSS keeps only a hash. */
2429
+ key: string;
2430
+ /** Tokens this request takes. Default 1. */
2431
+ cost?: number;
2432
+ }
2433
+ /** Result of `coss.risk.limit()`. */
2434
+ interface RiskLimit {
2435
+ object: "risk_limit";
2436
+ bucket: string;
2437
+ /** Whether to let the request through. Denied requests take no tokens. */
2438
+ allowed: boolean;
2439
+ /** The bucket's size (`burst`). */
2440
+ limit: number;
2441
+ /** Tokens left for this key. */
2442
+ remaining: number;
2443
+ /** Milliseconds until the key's bucket is full again. */
2444
+ reset_ms: number;
2445
+ /** When denied: milliseconds until the same request would fit; `null` if it never would. */
2446
+ retry_after_ms: number | null;
2447
+ /** COSS couldn't count: `allowed` is the bucket's `on_failure`. */
2448
+ degraded: boolean;
2449
+ }
2182
2450
  //#endregion
2183
2451
  //#region src/resources/risk.d.ts
2184
2452
  type ReadOptions$2 = Pick<RequestOptions, "signal">;
@@ -2241,6 +2509,22 @@ declare class RiskRulesResource {
2241
2509
  update(id: string, params: UpdateRiskRuleParams, options?: ReadOptions$2): Promise<CossResult<RiskRule>>;
2242
2510
  delete(id: string, options?: ReadOptions$2): Promise<CossResult<DeletedRiskRule>>;
2243
2511
  }
2512
+ /**
2513
+ * `coss.risk.limitBuckets`: your rate limit buckets, which `coss.risk.limit()` charges per key.
2514
+ */
2515
+ declare class RiskLimitBucketsResource {
2516
+ #private;
2517
+ /** @internal Use `new Coss().risk.limitBuckets`. */
2518
+ constructor(http: HttpClient);
2519
+ /** Creates a bucket. Not retried automatically: a retry would find its name taken. */
2520
+ create(params: CreateRiskLimitBucketParams, options?: ReadOptions$2): Promise<CossResult<RiskLimitBucket>>;
2521
+ /** Every bucket, by name. */
2522
+ list(options?: ReadOptions$2): Promise<CossResult<List<RiskLimitBucket>>>;
2523
+ get(id: string, options?: ReadOptions$2): Promise<CossResult<RiskLimitBucket>>;
2524
+ /** Changes the fields given; the next check uses them. */
2525
+ update(id: string, params: UpdateRiskLimitBucketParams, options?: ReadOptions$2): Promise<CossResult<RiskLimitBucket>>;
2526
+ delete(id: string, options?: ReadOptions$2): Promise<CossResult<DeletedRiskLimitBucket>>;
2527
+ }
2244
2528
  /** `coss.risk.users`: what COSS remembers about your users. */
2245
2529
  declare class RiskUsersResource {
2246
2530
  #private;
@@ -2273,6 +2557,8 @@ declare class RiskResource {
2273
2557
  readonly settings: RiskSettingsResource;
2274
2558
  /** Rules (`create`, `list`, `get`, `update`, `delete`). */
2275
2559
  readonly rules: RiskRulesResource;
2560
+ /** Your rate limit buckets (`create`, `list`, `get`, `update`, `delete`). */
2561
+ readonly limitBuckets: RiskLimitBucketsResource;
2276
2562
  /** @internal Use `new Coss().risk`. */
2277
2563
  constructor(http: HttpClient);
2278
2564
  /**
@@ -2282,9 +2568,42 @@ declare class RiskResource {
2282
2568
  assess(params: AssessRiskParams, options?: RequestOptions): Promise<CossResult<RiskAssessment>>;
2283
2569
  /**
2284
2570
  * Reports what an assessed user turned out to be: `fraud` and `abuse` teach later assessments to
2285
- * flag the same mailbox, user, IP and subnet; `legitimate` takes that back. Resolves a review.
2571
+ * flag the same mailbox, user, card, device, IP and subnet; `legitimate` takes that back.
2572
+ * Resolves a review, and counts in `calibration()`.
2286
2573
  */
2287
2574
  feedback(id: string, params: RiskFeedbackParams, options?: RequestOptions): Promise<CossResult<RiskAssessment>>;
2575
+ /**
2576
+ * How often each reason and rule was right over the last 90 days, by your feedback, and the
2577
+ * reason weights that supports. Apply them with `settings.update({ reason_weights })`.
2578
+ */
2579
+ calibration(options?: ReadOptions$2): Promise<CossResult<RiskCalibration>>;
2580
+ /**
2581
+ * Checks an email address on its own: syntax, disposable domains, deliverability, role
2582
+ * accounts, free providers, the canonical mailbox, typos and domain age. Nothing is stored.
2583
+ */
2584
+ checkEmail(email: string, options?: ReadOptions$2): Promise<CossResult<RiskEmailCheck>>;
2585
+ /**
2586
+ * Checks an IP address on its own: Tor, cloud, hosting and VPN networks, ASN and country, and
2587
+ * VPN, proxy and relay exits when COSS's privacy data is available.
2588
+ */
2589
+ checkIp(ip: string, options?: ReadOptions$2): Promise<CossResult<RiskIpCheck>>;
2590
+ /**
2591
+ * Scores text for spam, phishing, malware and fraud. Scores only, never a verdict: you choose
2592
+ * the thresholds. The text is neither stored nor logged.
2593
+ */
2594
+ classify(params: {
2595
+ text: string;
2596
+ }, options?: ReadOptions$2): Promise<CossResult<RiskContentClassification>>;
2597
+ /**
2598
+ * Takes `cost` tokens (default 1) from `key`'s share of one of your buckets
2599
+ * (`coss.risk.limitBuckets`). Retries reuse one `Idempotency-Key`, so they are charged once.
2600
+ *
2601
+ * ```ts
2602
+ * const { data } = await coss.risk.limit({ bucket: "password_reset", key: email });
2603
+ * if (data && !data.allowed) return tooManyRequests(data.retry_after_ms);
2604
+ * ```
2605
+ */
2606
+ limit(params: CheckRiskLimitParams, options?: RequestOptions): Promise<CossResult<RiskLimit>>;
2288
2607
  }
2289
2608
  //#endregion
2290
2609
  //#region src/types/webhook.d.ts
@@ -2779,7 +3098,7 @@ export declare class Coss {
2779
3098
  //#endregion
2780
3099
  //#region src/version.d.ts
2781
3100
  /** SDK version, sent in the User-Agent. A test keeps it equal to package.json's `version`. */
2782
- export declare const VERSION = "0.2.0";
3101
+ export declare const VERSION = "0.3.0";
2783
3102
  //#endregion
2784
3103
  //#region src/webhooks.d.ts
2785
3104
  /** A webhook request failed verification: reject it (e.g. respond 400). */
@@ -2820,4 +3139,4 @@ export declare function verifyWebhook<T = WebhookEvent>(payload: string, headers
2820
3139
  */
2821
3140
  export declare function verifyLogDrainBatch(payload: string, headers: WebhookHeaders, secret: string, options?: VerifyWebhookOptions): Promise<LogDrainLine[]>;
2822
3141
  //#endregion
2823
- export type { AccountEventType, AccountWebhookEvent, AccountWebhookEventData, ApiVersion, AssessRiskParams, AuditActorType, AuditEvent, AuditEventsResource, Billing, BillingCreditsResource, BillingEventType, BillingHoldReason, BillingInvoicesResource, BillingResource, BillingWebhookEvent, CheckSuppressionParams, CossError, CossErrorIssue, CossErrorType, CossOptions, CossResult, CreateDomainParams, CreateHookApplicationParams, CreateHookEndpointParams, CreateHookEventTypeParams, CreateHookPortalSessionParams, CreateLogDrainParams, CreateReceivingDomainParams, CreateRiskListEntryParams, CreateRiskRuleParams, CreateSuppressionParams, CreateTrackingDomainParams, CreateWebhookEndpointParams, Credit, DeletedDomain, DeletedHookApplication, DeletedHookEndpoint, DeletedLogDrain, DeletedReceivedEmail, DeletedRiskListEntry, DeletedRiskRule, DeletedSuppression, DeletedTrackingRule, DeletedWebhookEndpoint, Domain, DomainRecord, DomainRecordPurpose, DomainRecordStatus, DomainStatus, DomainsResource, Email, EmailAttachment, EmailAttachmentParams, EmailBatch, EmailEvent, EmailEventData, EmailEventType, EmailLink, EmailRecipient, EmailRecipientStatus, EmailResource, EmailStatus, EmailTag, EmailTracking, EmailWebhookEvent, EmailWebhookEventData, ExportSuppressionsParams, FetchLike, ForgottenRiskUser, HookApplication, HookApplicationsResource, HookAttempt, HookDeliveriesResource, HookDelivery, HookDeliveryStatus, HookEndpoint, HookEndpointDisabledReason, HookEndpointRecovery, HookEndpointSecret, HookEndpointWebhookEvent, HookEndpointWebhookEventData, HookEndpointWithSecret, HookEndpointsResource, HookEventType, HookEventTypesResource, HookMessage, HookMessagesResource, HookOperationalEventType, HookPortalResource, HookPortalSession, HooksResource, ImportSuppressionEntry, ImportSuppressionsParams, InboundAddress, Invoice, InvoiceLine, InvoiceLineKind, InvoiceStatus, InvoiceSummary, List, ListAuditEventsParams, ListCreditsParams, ListHookApplicationsParams, ListHookDeliveriesParams, ListHookEventTypesParams, ListHookMessagesParams, ListInvoicesParams, ListLogDrainBatchesParams, ListLogsParams, ListReceivedEmailsParams, ListRiskAssessmentsParams, ListRiskListEntriesParams, ListSuppressionsParams, ListUsageParams, ListWebhookDeliveriesParams, Log, LogDrain, LogDrainAttempt, LogDrainBatch, LogDrainBatchStatus, LogDrainBatchesResource, LogDrainLine, LogDrainSource, LogDrainWithSecret, LogDrainsResource, LogLevel, LogSource, LogsResource, MailFromStatus, Meter, PlanKey, ReceivedEmail, ReceivedEmailAttachment, ReceivedEmailEventType, ReceivedEmailStatus, ReceivedEmailSummary, ReceivedEmailVerdicts, ReceivedEmailWebhookEvent, ReceivedEmailWebhookEventData, ReceivedEmailsResource, ReceivingDomain, ReceivingDomainRecord, ReceivingDomainsResource, RecoverHookEndpointParams, RecoverWebhookEndpointParams, Reputation, ReputationCheck, ReputationCheckSeverity, ReputationResource, ReputationStatus, RequestOptions, RescheduleEmailParams, RiskAssessment, RiskAssessmentsResource, RiskCaptchaProvider, RiskCaptchaSignals, RiskDecision, RiskDegradedSignal, RiskEmailSignals, RiskEvent, RiskEventType, RiskFeedbackParams, RiskIpSignals, RiskList, RiskListEntry, RiskListEntryType, RiskListEntryValue, RiskListsResource, RiskLoginSignals, RiskOutcome, RiskReason, RiskReasonCode, RiskResource, RiskRule, RiskRuleCondition, RiskRuleField, RiskRulesResource, RiskSettings, RiskSettingsResource, RiskUsersResource, RiskWebhookEvent, RiskWebhookEventData, RollHookEndpointSecretParams, RollWebhookEndpointSecretParams, SendEmailParams, SendHookMessageParams, SendHookTestParams, Suppression, SuppressionCheck, SuppressionImport, SuppressionReason, SuppressionResource, TrackingDomain, TrackingDomainRecord, TrackingDomainStatus, TrackingDomainsResource, TrackingOptions, TrackingResource, TrackingRule, TrackingSettings, UpdateHookApplicationParams, UpdateHookEndpointParams, UpdateHookEventTypeParams, UpdateLogDrainParams, UpdateRiskRuleParams, UpdateRiskSettingsParams, UpdateTrackingRuleParams, UpdateWebhookEndpointParams, Usage, VerdictStatus, VerifyWebhookOptions, WebhookAttempt, WebhookDeliveriesResource, WebhookDelivery, WebhookDeliveryStatus, WebhookEndpoint, WebhookEndpointDisabledReason, WebhookEndpointRecovery, WebhookEndpointWithSecret, WebhookEndpointsResource, WebhookEvent, WebhookEventType, WebhookHeaders };
3142
+ export type { AccountEventType, AccountWebhookEvent, AccountWebhookEventData, ApiVersion, AssessRiskParams, AuditActorType, AuditEvent, AuditEventsResource, Billing, BillingCreditsResource, BillingEventType, BillingHoldReason, BillingInvoicesResource, BillingResource, BillingWebhookEvent, CheckRiskLimitParams, CheckSuppressionParams, CossError, CossErrorIssue, CossErrorType, CossOptions, CossResult, CreateDomainParams, CreateHookApplicationParams, CreateHookEndpointParams, CreateHookEventTypeParams, CreateHookPortalSessionParams, CreateLogDrainParams, CreateReceivingDomainParams, CreateRiskLimitBucketParams, CreateRiskListEntryParams, CreateRiskRuleParams, CreateSuppressionParams, CreateTrackingDomainParams, CreateWebhookEndpointParams, Credit, DeletedDomain, DeletedHookApplication, DeletedHookEndpoint, DeletedLogDrain, DeletedReceivedEmail, DeletedRiskLimitBucket, DeletedRiskListEntry, DeletedRiskRule, DeletedSuppression, DeletedTrackingRule, DeletedWebhookEndpoint, Domain, DomainRecord, DomainRecordPurpose, DomainRecordStatus, DomainStatus, DomainsResource, Email, EmailAttachment, EmailAttachmentParams, EmailBatch, EmailEvent, EmailEventData, EmailEventType, EmailFailure, EmailLink, EmailRecipient, EmailRecipientStatus, EmailResource, EmailStatus, EmailSummary, EmailTag, EmailTracking, EmailWebhookEvent, EmailWebhookEventData, ExportSuppressionsParams, FetchLike, ForgottenRiskUser, HookApplication, HookApplicationsResource, HookAttempt, HookDeliveriesResource, HookDelivery, HookDeliveryStatus, HookEndpoint, HookEndpointDisabledReason, HookEndpointRecovery, HookEndpointSecret, HookEndpointWebhookEvent, HookEndpointWebhookEventData, HookEndpointWithSecret, HookEndpointsResource, HookEventType, HookEventTypesResource, HookMessage, HookMessagesResource, HookOperationalEventType, HookPortalResource, HookPortalSession, HooksResource, ImportSuppressionEntry, ImportSuppressionsParams, InboundAddress, Invoice, InvoiceLine, InvoiceLineKind, InvoiceStatus, InvoiceSummary, List, ListAuditEventsParams, ListCreditsParams, ListEmailsParams, ListHookApplicationsParams, ListHookDeliveriesParams, ListHookEventTypesParams, ListHookMessagesParams, ListInvoicesParams, ListLogDrainBatchesParams, ListLogsParams, ListReceivedEmailsParams, ListRiskAssessmentsParams, ListRiskListEntriesParams, ListSuppressionsParams, ListUsageParams, ListWebhookDeliveriesParams, Log, LogDrain, LogDrainAttempt, LogDrainBatch, LogDrainBatchStatus, LogDrainBatchesResource, LogDrainLine, LogDrainSource, LogDrainWithSecret, LogDrainsResource, LogLevel, LogSource, LogsResource, MailFromStatus, Meter, PlanKey, ReceivedEmail, ReceivedEmailAttachment, ReceivedEmailEventType, ReceivedEmailStatus, ReceivedEmailSummary, ReceivedEmailVerdicts, ReceivedEmailWebhookEvent, ReceivedEmailWebhookEventData, ReceivedEmailsResource, ReceivingDomain, ReceivingDomainRecord, ReceivingDomainsResource, RecoverHookEndpointParams, RecoverWebhookEndpointParams, Reputation, ReputationCheck, ReputationCheckSeverity, ReputationResource, ReputationStatus, RequestOptions, RescheduleEmailParams, RiskAssessment, RiskAssessmentsResource, RiskCalibration, RiskCaptchaProvider, RiskCaptchaSignals, RiskCardFunding, RiskCheckoutSignals, RiskContentCategory, RiskContentClassification, RiskContentScores, RiskDecision, RiskDegradedSignal, RiskDeviceSignals, RiskEmailCheck, RiskEmailSignals, RiskEvent, RiskEventType, RiskFeedbackParams, RiskIpCheck, RiskIpPrivacy, RiskIpSignals, RiskLimit, RiskLimitBucket, RiskLimitBucketsResource, RiskLimitFailureMode, RiskList, RiskListEntry, RiskListEntryType, RiskListEntryValue, RiskListsResource, RiskLoginSignals, RiskOutcome, RiskReason, RiskReasonCode, RiskResource, RiskRule, RiskRuleCondition, RiskRuleField, RiskRulesResource, RiskSettings, RiskSettingsResource, RiskUsersResource, RiskWebhookEvent, RiskWebhookEventData, RollHookEndpointSecretParams, RollWebhookEndpointSecretParams, SendEmailParams, SendHookMessageParams, SendHookTestParams, Suppression, SuppressionCheck, SuppressionImport, SuppressionReason, SuppressionResource, TrackingDomain, TrackingDomainRecord, TrackingDomainStatus, TrackingDomainsResource, TrackingOptions, TrackingResource, TrackingRule, TrackingSettings, UpdateHookApplicationParams, UpdateHookEndpointParams, UpdateHookEventTypeParams, UpdateLogDrainParams, UpdateRiskLimitBucketParams, UpdateRiskRuleParams, UpdateRiskSettingsParams, UpdateTrackingRuleParams, UpdateWebhookEndpointParams, Usage, VerdictStatus, VerifyWebhookOptions, WebhookAttempt, WebhookDeliveriesResource, WebhookDelivery, WebhookDeliveryStatus, WebhookEndpoint, WebhookEndpointDisabledReason, WebhookEndpointRecovery, WebhookEndpointWithSecret, WebhookEndpointsResource, WebhookEvent, WebhookEventType, WebhookHeaders };
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ const API_VERSION = "2026-10-10";
9
9
  //#endregion
10
10
  //#region src/version.ts
11
11
  /** SDK version, sent in the User-Agent. A test keeps it equal to package.json's `version`. */
12
- const VERSION = "0.2.0";
12
+ const VERSION = "0.3.0";
13
13
  //#endregion
14
14
  //#region src/core/errors.ts
15
15
  const API_ERROR_TYPES = /* @__PURE__ */ new Set([
@@ -826,6 +826,19 @@ var EmailResource = class {
826
826
  });
827
827
  }
828
828
  /**
829
+ * Lists emails sent from the same environment as the API key, newest first and without their
830
+ * content (`get` one for `html`, `text`, `headers` and `attachments`). Pass the last `id` as
831
+ * `after` while `has_more` is true.
832
+ */
833
+ list(params = {}, options) {
834
+ return this.#http.request({
835
+ method: "GET",
836
+ path: "/v1/emails",
837
+ query: listQuery(params),
838
+ signal: options?.signal
839
+ });
840
+ }
841
+ /**
829
842
  * Cancels a scheduled email. Canceling an already canceled email returns it; any other email
830
843
  * fails with `email_not_cancelable`.
831
844
  */
@@ -1521,6 +1534,57 @@ var RiskRulesResource = class {
1521
1534
  });
1522
1535
  }
1523
1536
  };
1537
+ /**
1538
+ * `coss.risk.limitBuckets`: your rate limit buckets, which `coss.risk.limit()` charges per key.
1539
+ */
1540
+ var RiskLimitBucketsResource = class {
1541
+ #http;
1542
+ /** @internal Use `new Coss().risk.limitBuckets`. */
1543
+ constructor(http) {
1544
+ this.#http = http;
1545
+ }
1546
+ /** Creates a bucket. Not retried automatically: a retry would find its name taken. */
1547
+ create(params, options) {
1548
+ return this.#http.request({
1549
+ method: "POST",
1550
+ path: "/v1/risk/limit_buckets",
1551
+ body: params,
1552
+ retry: false,
1553
+ signal: options?.signal
1554
+ });
1555
+ }
1556
+ /** Every bucket, by name. */
1557
+ list(options) {
1558
+ return this.#http.request({
1559
+ method: "GET",
1560
+ path: "/v1/risk/limit_buckets",
1561
+ signal: options?.signal
1562
+ });
1563
+ }
1564
+ get(id, options) {
1565
+ return this.#http.request({
1566
+ method: "GET",
1567
+ path: `/v1/risk/limit_buckets/${encodeURIComponent(id)}`,
1568
+ signal: options?.signal
1569
+ });
1570
+ }
1571
+ /** Changes the fields given; the next check uses them. */
1572
+ update(id, params, options) {
1573
+ return this.#http.request({
1574
+ method: "PATCH",
1575
+ path: `/v1/risk/limit_buckets/${encodeURIComponent(id)}`,
1576
+ body: params,
1577
+ signal: options?.signal
1578
+ });
1579
+ }
1580
+ delete(id, options) {
1581
+ return this.#http.request({
1582
+ method: "DELETE",
1583
+ path: `/v1/risk/limit_buckets/${encodeURIComponent(id)}`,
1584
+ signal: options?.signal
1585
+ });
1586
+ }
1587
+ };
1524
1588
  /** `coss.risk.users`: what COSS remembers about your users. */
1525
1589
  var RiskUsersResource = class {
1526
1590
  #http;
@@ -1560,6 +1624,8 @@ var RiskResource = class {
1560
1624
  settings;
1561
1625
  /** Rules (`create`, `list`, `get`, `update`, `delete`). */
1562
1626
  rules;
1627
+ /** Your rate limit buckets (`create`, `list`, `get`, `update`, `delete`). */
1628
+ limitBuckets;
1563
1629
  #http;
1564
1630
  /** @internal Use `new Coss().risk`. */
1565
1631
  constructor(http) {
@@ -1569,6 +1635,7 @@ var RiskResource = class {
1569
1635
  this.users = new RiskUsersResource(http);
1570
1636
  this.settings = new RiskSettingsResource(http);
1571
1637
  this.rules = new RiskRulesResource(http);
1638
+ this.limitBuckets = new RiskLimitBucketsResource(http);
1572
1639
  }
1573
1640
  /**
1574
1641
  * Assesses an event and returns the decision with its score and reasons. Retries reuse one
@@ -1585,7 +1652,8 @@ var RiskResource = class {
1585
1652
  }
1586
1653
  /**
1587
1654
  * Reports what an assessed user turned out to be: `fraud` and `abuse` teach later assessments to
1588
- * flag the same mailbox, user, IP and subnet; `legitimate` takes that back. Resolves a review.
1655
+ * flag the same mailbox, user, card, device, IP and subnet; `legitimate` takes that back.
1656
+ * Resolves a review, and counts in `calibration()`.
1589
1657
  */
1590
1658
  feedback(id, params, options) {
1591
1659
  return this.#http.request({
@@ -1596,6 +1664,71 @@ var RiskResource = class {
1596
1664
  signal: options?.signal
1597
1665
  });
1598
1666
  }
1667
+ /**
1668
+ * How often each reason and rule was right over the last 90 days, by your feedback, and the
1669
+ * reason weights that supports. Apply them with `settings.update({ reason_weights })`.
1670
+ */
1671
+ calibration(options) {
1672
+ return this.#http.request({
1673
+ method: "GET",
1674
+ path: "/v1/risk/calibration",
1675
+ signal: options?.signal
1676
+ });
1677
+ }
1678
+ /**
1679
+ * Checks an email address on its own: syntax, disposable domains, deliverability, role
1680
+ * accounts, free providers, the canonical mailbox, typos and domain age. Nothing is stored.
1681
+ */
1682
+ checkEmail(email, options) {
1683
+ return this.#http.request({
1684
+ method: "POST",
1685
+ path: "/v1/risk/email/check",
1686
+ body: { email },
1687
+ signal: options?.signal
1688
+ });
1689
+ }
1690
+ /**
1691
+ * Checks an IP address on its own: Tor, cloud, hosting and VPN networks, ASN and country, and
1692
+ * VPN, proxy and relay exits when COSS's privacy data is available.
1693
+ */
1694
+ checkIp(ip, options) {
1695
+ return this.#http.request({
1696
+ method: "POST",
1697
+ path: "/v1/risk/ip/check",
1698
+ body: { ip },
1699
+ signal: options?.signal
1700
+ });
1701
+ }
1702
+ /**
1703
+ * Scores text for spam, phishing, malware and fraud. Scores only, never a verdict: you choose
1704
+ * the thresholds. The text is neither stored nor logged.
1705
+ */
1706
+ classify(params, options) {
1707
+ return this.#http.request({
1708
+ method: "POST",
1709
+ path: "/v1/risk/content/classify",
1710
+ body: params,
1711
+ signal: options?.signal
1712
+ });
1713
+ }
1714
+ /**
1715
+ * Takes `cost` tokens (default 1) from `key`'s share of one of your buckets
1716
+ * (`coss.risk.limitBuckets`). Retries reuse one `Idempotency-Key`, so they are charged once.
1717
+ *
1718
+ * ```ts
1719
+ * const { data } = await coss.risk.limit({ bucket: "password_reset", key: email });
1720
+ * if (data && !data.allowed) return tooManyRequests(data.retry_after_ms);
1721
+ * ```
1722
+ */
1723
+ limit(params, options) {
1724
+ return this.#http.request({
1725
+ method: "POST",
1726
+ path: "/v1/risk/limits",
1727
+ body: params,
1728
+ idempotencyKey: options?.idempotencyKey,
1729
+ signal: options?.signal
1730
+ });
1731
+ }
1599
1732
  };
1600
1733
  //#endregion
1601
1734
  //#region src/resources/webhook-deliveries.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coss",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "The official TypeScript SDK for COSS: email, SMS, video, scheduling and more behind one API key.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://coss.com/docs",
@@ -23,6 +23,7 @@
23
23
  "types": "./dist/index.d.ts",
24
24
  "exports": {
25
25
  ".": "./dist/index.js",
26
+ "./device": "./dist/device.js",
26
27
  "./package.json": "./package.json"
27
28
  },
28
29
  "scripts": {