Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "4.3.0"
".": "4.4.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 77
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-59c9efb45644c91ba21c8efaa0af53637071483ed661d5f99f27a15996624ac2.yml
openapi_spec_hash: 0e1d703ca5e68a4d1f1489c04eb43765
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-9859cb664d1a9de0323c857e96cf41d0a5ac5c5903c501059f36bf62553b1583.yml
openapi_spec_hash: d29149d60504eba35c63f583ce4bf0bc
config_hash: 3ec521d062b05b81c22bc1a25bfe3d02
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 4.4.0 (2025-12-16)

Full Changelog: [v4.3.0...v4.4.0](https://github.com/trycourier/courier-java/compare/v4.3.0...v4.4.0)

### Features

* Add timezone field to Delay schema ([46d1671](https://github.com/trycourier/courier-java/commit/46d16713aeb86a15fe73de9c9e2aea168872a7c0))
* Update bulk API spec: make event required, document profile.email req… ([e436dcd](https://github.com/trycourier/courier-java/commit/e436dcd0a916911f366d3016333b8a6a2cdc947d))

## 4.3.0 (2025-12-08)

Full Changelog: [v4.2.0...v4.3.0](https://github.com/trycourier/courier-java/compare/v4.2.0...v4.3.0)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

<!-- x-release-please-start-version -->

[![Maven Central](https://img.shields.io/maven-central/v/com.courier/courier-java)](https://central.sonatype.com/artifact/com.courier/courier-java/4.3.0)
[![javadoc](https://javadoc.io/badge2/com.courier/courier-java/4.3.0/javadoc.svg)](https://javadoc.io/doc/com.courier/courier-java/4.3.0)
[![Maven Central](https://img.shields.io/maven-central/v/com.courier/courier-java)](https://central.sonatype.com/artifact/com.courier/courier-java/4.4.0)
[![javadoc](https://javadoc.io/badge2/com.courier/courier-java/4.4.0/javadoc.svg)](https://javadoc.io/doc/com.courier/courier-java/4.4.0)

<!-- x-release-please-end -->

Expand All @@ -13,7 +13,7 @@ It is generated with [Stainless](https://www.stainless.com/).

<!-- x-release-please-start-version -->

The REST API documentation can be found on [www.courier.com](https://www.courier.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.courier/courier-java/4.3.0).
The REST API documentation can be found on [www.courier.com](https://www.courier.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.courier/courier-java/4.4.0).

<!-- x-release-please-end -->

Expand All @@ -24,7 +24,7 @@ The REST API documentation can be found on [www.courier.com](https://www.courier
### Gradle

```kotlin
implementation("com.courier:courier-java:4.3.0")
implementation("com.courier:courier-java:4.4.0")
```

### Maven
Expand All @@ -33,7 +33,7 @@ implementation("com.courier:courier-java:4.3.0")
<dependency>
<groupId>com.courier</groupId>
<artifactId>courier-java</artifactId>
<version>4.3.0</version>
<version>4.4.0</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repositories {

allprojects {
group = "com.courier"
version = "4.3.0" // x-release-please-version
version = "4.4.0" // x-release-please-version
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import java.util.Objects
import java.util.Optional
import kotlin.jvm.optionals.getOrNull

/** Ingest user data into a Bulk Job */
/**
* Ingest user data into a Bulk Job.
*
* **Important**: For email-based bulk jobs, each user must include `profile.email` for provider
* routing to work correctly. The `to.email` field is not sufficient for email provider routing.
*/
class BulkAddUsersParams
private constructor(
private val jobId: String?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ import java.util.Collections
import java.util.Objects
import kotlin.jvm.optionals.getOrNull

/** Create a bulk job */
/**
* Creates a new bulk job for sending messages to multiple recipients.
*
* **Required**: `message.event` (event ID or notification ID)
*
* **Optional (V2 format)**: `message.template` (notification ID) or `message.content` (Elemental
* content) can be provided to override the notification associated with the event.
*/
class BulkCreateJobParams
private constructor(
private val body: Body,
Expand All @@ -28,6 +35,11 @@ private constructor(
) : Params {

/**
* Bulk message definition. Supports two formats:
* - V1 format: Requires `event` field (event ID or notification ID)
* - V2 format: Optionally use `template` (notification ID) or `content` (Elemental content) in
* addition to `event`
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
*/
Expand Down Expand Up @@ -86,6 +98,12 @@ private constructor(
*/
fun body(body: Body) = apply { this.body = body.toBuilder() }

/**
* Bulk message definition. Supports two formats:
* - V1 format: Requires `event` field (event ID or notification ID)
* - V2 format: Optionally use `template` (notification ID) or `content` (Elemental content)
* in addition to `event`
*/
fun message(message: InboundBulkMessage) = apply { body.message(message) }

/**
Expand All @@ -97,16 +115,6 @@ private constructor(
*/
fun message(message: JsonField<InboundBulkMessage>) = apply { body.message(message) }

/** Alias for calling [message] with `InboundBulkMessage.ofTemplate(template)`. */
fun message(template: InboundBulkMessage.InboundBulkTemplateMessage) = apply {
body.message(template)
}

/** Alias for calling [message] with `InboundBulkMessage.ofContent(content)`. */
fun message(content: InboundBulkMessage.InboundBulkContentMessage) = apply {
body.message(content)
}

fun additionalBodyProperties(additionalBodyProperties: Map<String, JsonValue>) = apply {
body.additionalProperties(additionalBodyProperties)
}
Expand Down Expand Up @@ -265,6 +273,11 @@ private constructor(
) : this(message, mutableMapOf())

/**
* Bulk message definition. Supports two formats:
* - V1 format: Requires `event` field (event ID or notification ID)
* - V2 format: Optionally use `template` (notification ID) or `content` (Elemental content)
* in addition to `event`
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
*/
Expand Down Expand Up @@ -316,6 +329,12 @@ private constructor(
additionalProperties = body.additionalProperties.toMutableMap()
}

/**
* Bulk message definition. Supports two formats:
* - V1 format: Requires `event` field (event ID or notification ID)
* - V2 format: Optionally use `template` (notification ID) or `content` (Elemental
* content) in addition to `event`
*/
fun message(message: InboundBulkMessage) = message(JsonField.of(message))

/**
Expand All @@ -327,14 +346,6 @@ private constructor(
*/
fun message(message: JsonField<InboundBulkMessage>) = apply { this.message = message }

/** Alias for calling [message] with `InboundBulkMessage.ofTemplate(template)`. */
fun message(template: InboundBulkMessage.InboundBulkTemplateMessage) =
message(InboundBulkMessage.ofTemplate(template))

/** Alias for calling [message] with `InboundBulkMessage.ofContent(content)`. */
fun message(content: InboundBulkMessage.InboundBulkContentMessage) =
message(InboundBulkMessage.ofContent(content))

fun additionalProperties(additionalProperties: Map<String, JsonValue>) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ private constructor(
private constructor(
private val data: JsonValue,
private val preferences: JsonField<RecipientPreferences>,
private val profile: JsonValue,
private val profile: JsonField<InboundBulkMessageUser.Profile>,
private val recipient: JsonField<String>,
private val to: JsonField<UserRecipient>,
private val status: JsonField<Status>,
Expand All @@ -225,7 +225,9 @@ private constructor(
@JsonProperty("preferences")
@ExcludeMissing
preferences: JsonField<RecipientPreferences> = JsonMissing.of(),
@JsonProperty("profile") @ExcludeMissing profile: JsonValue = JsonMissing.of(),
@JsonProperty("profile")
@ExcludeMissing
profile: JsonField<InboundBulkMessageUser.Profile> = JsonMissing.of(),
@JsonProperty("recipient")
@ExcludeMissing
recipient: JsonField<String> = JsonMissing.of(),
Expand All @@ -245,6 +247,7 @@ private constructor(
.to(to)
.build()

/** User-specific data that will be merged with message.data */
@JsonProperty("data") @ExcludeMissing fun _data(): JsonValue = data

/**
Expand All @@ -253,15 +256,29 @@ private constructor(
*/
fun preferences(): Optional<RecipientPreferences> = preferences.getOptional("preferences")

@JsonProperty("profile") @ExcludeMissing fun _profile(): JsonValue = profile
/**
* User profile information. For email-based bulk jobs, `profile.email` is required for
* provider routing to determine if the message can be delivered. The email address should
* be provided here rather than in `to.email`.
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
fun profile(): Optional<InboundBulkMessageUser.Profile> = profile.getOptional("profile")

/**
* User ID (legacy field, use profile or to.user_id instead)
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
fun recipient(): Optional<String> = recipient.getOptional("recipient")

/**
* Optional recipient information. Note: For email provider routing, use `profile.email`
* instead of `to.email`. The `to` field is primarily used for recipient identification and
* data merging.
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
Expand All @@ -288,6 +305,15 @@ private constructor(
@ExcludeMissing
fun _preferences(): JsonField<RecipientPreferences> = preferences

/**
* Returns the raw JSON value of [profile].
*
* Unlike [profile], this method doesn't throw if the JSON field has an unexpected type.
*/
@JsonProperty("profile")
@ExcludeMissing
fun _profile(): JsonField<InboundBulkMessageUser.Profile> = profile

/**
* Returns the raw JSON value of [recipient].
*
Expand Down Expand Up @@ -346,7 +372,7 @@ private constructor(

private var data: JsonValue = JsonMissing.of()
private var preferences: JsonField<RecipientPreferences> = JsonMissing.of()
private var profile: JsonValue = JsonMissing.of()
private var profile: JsonField<InboundBulkMessageUser.Profile> = JsonMissing.of()
private var recipient: JsonField<String> = JsonMissing.of()
private var to: JsonField<UserRecipient> = JsonMissing.of()
private var status: JsonField<Status>? = null
Expand All @@ -365,6 +391,7 @@ private constructor(
additionalProperties = item.additionalProperties.toMutableMap()
}

/** User-specific data that will be merged with message.data */
fun data(data: JsonValue) = apply { this.data = data }

fun preferences(preferences: RecipientPreferences?) =
Expand All @@ -385,8 +412,30 @@ private constructor(
this.preferences = preferences
}

fun profile(profile: JsonValue) = apply { this.profile = profile }
/**
* User profile information. For email-based bulk jobs, `profile.email` is required for
* provider routing to determine if the message can be delivered. The email address
* should be provided here rather than in `to.email`.
*/
fun profile(profile: InboundBulkMessageUser.Profile?) =
profile(JsonField.ofNullable(profile))

/** Alias for calling [Builder.profile] with `profile.orElse(null)`. */
fun profile(profile: Optional<InboundBulkMessageUser.Profile>) =
profile(profile.getOrNull())

/**
* Sets [Builder.profile] to an arbitrary JSON value.
*
* You should usually call [Builder.profile] with a well-typed
* [InboundBulkMessageUser.Profile] value instead. This method is primarily for setting
* the field to an undocumented or not yet supported value.
*/
fun profile(profile: JsonField<InboundBulkMessageUser.Profile>) = apply {
this.profile = profile
}

/** User ID (legacy field, use profile or to.user_id instead) */
fun recipient(recipient: String?) = recipient(JsonField.ofNullable(recipient))

/** Alias for calling [Builder.recipient] with `recipient.orElse(null)`. */
Expand All @@ -401,6 +450,11 @@ private constructor(
*/
fun recipient(recipient: JsonField<String>) = apply { this.recipient = recipient }

/**
* Optional recipient information. Note: For email provider routing, use `profile.email`
* instead of `to.email`. The `to` field is primarily used for recipient identification
* and data merging.
*/
fun to(to: UserRecipient?) = to(JsonField.ofNullable(to))

/** Alias for calling [Builder.to] with `to.orElse(null)`. */
Expand Down Expand Up @@ -492,6 +546,7 @@ private constructor(
}

preferences().ifPresent { it.validate() }
profile().ifPresent { it.validate() }
recipient()
to().ifPresent { it.validate() }
status().validate()
Expand All @@ -516,6 +571,7 @@ private constructor(
@JvmSynthetic
internal fun validity(): Int =
(preferences.asKnown().getOrNull()?.validity() ?: 0) +
(profile.asKnown().getOrNull()?.validity() ?: 0) +
(if (recipient.asKnown().isPresent) 1 else 0) +
(to.asKnown().getOrNull()?.validity() ?: 0) +
(status.asKnown().getOrNull()?.validity() ?: 0) +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ private constructor(
) : this(definition, enqueued, failures, received, status, mutableMapOf())

/**
* Bulk message definition. Supports two formats:
* - V1 format: Requires `event` field (event ID or notification ID)
* - V2 format: Optionally use `template` (notification ID) or `content` (Elemental content)
* in addition to `event`
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
*/
Expand Down Expand Up @@ -288,6 +293,12 @@ private constructor(
additionalProperties = job.additionalProperties.toMutableMap()
}

/**
* Bulk message definition. Supports two formats:
* - V1 format: Requires `event` field (event ID or notification ID)
* - V2 format: Optionally use `template` (notification ID) or `content` (Elemental
* content) in addition to `event`
*/
fun definition(definition: InboundBulkMessage) = definition(JsonField.of(definition))

/**
Expand All @@ -301,14 +312,6 @@ private constructor(
this.definition = definition
}

/** Alias for calling [definition] with `InboundBulkMessage.ofTemplate(template)`. */
fun definition(template: InboundBulkMessage.InboundBulkTemplateMessage) =
definition(InboundBulkMessage.ofTemplate(template))

/** Alias for calling [definition] with `InboundBulkMessage.ofContent(content)`. */
fun definition(content: InboundBulkMessage.InboundBulkContentMessage) =
definition(InboundBulkMessage.ofContent(content))

fun enqueued(enqueued: Long) = enqueued(JsonField.of(enqueued))

/**
Expand Down
Loading