adapty 0.8.1 → 0.8.2

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.
package/README.md CHANGED
@@ -137,11 +137,15 @@ Campaign structure. These lists return metadata only — numbers come from `asa
137
137
  adapty asa campaigns list
138
138
  adapty asa campaigns get CAMPAIGN_ID
139
139
  adapty asa campaigns create --org UUID --name "Winter push" --adam-id 123456 --country US --daily-budget 50
140
+ adapty asa campaigns create --org UUID --name "LOC push" --adam-id 123456 --country US --daily-budget 50 \
141
+ --invoice-advertiser "Acme Inc" --invoice-order-number PO-42 --invoice-contact-name "Jane Doe" \
142
+ --invoice-contact-email jane@acme.com --invoice-billing-email billing@acme.com # payment_model LOC
140
143
  adapty asa campaigns update CAMPAIGN_ID [--status PAUSED] [--daily-budget 80] [--country US]
141
144
 
142
145
  adapty asa ad-groups list
143
146
  adapty asa ad-groups get AD_GROUP_ID
144
147
  adapty asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20
148
+ adapty asa ad-groups create --campaign UUID --name "Automated Max Conv" --automated # Max Conversions campaigns
145
149
  adapty asa ad-groups update AD_GROUP_ID [--default-bid 1.50] [--status PAUSED]
146
150
 
147
151
  adapty asa ads list
@@ -5,6 +5,7 @@ export default class AsaAdGroupsCreate extends Command {
5
5
  static enableJsonFlag: boolean;
6
6
  static examples: string[];
7
7
  static flags: {
8
+ automated: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
9
  'automated-keywords': import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
10
  campaign: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
11
  'cpa-goal': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -5,11 +5,12 @@ import { currencyFlag, idempotencyFlags, money, moneyFlag, pricingModelFlag, sch
5
5
  import { isValidUuid } from '../../../lib/flags.js';
6
6
  import { printResponse } from '../../../lib/output.js';
7
7
  export default class AsaAdGroupsCreate extends Command {
8
- static description = 'Create an ad group inside a campaign';
8
+ static description = 'Create an ad group inside a campaign; --automated creates the automated ad group a Max Conversions campaign needs to run';
9
9
  static enableJsonFlag = true;
10
10
  static examples = [
11
11
  '<%= config.bin %> asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20',
12
12
  '<%= config.bin %> asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20 --start-time 2026-09-01 --pricing-model CPM',
13
+ '<%= config.bin %> asa ad-groups create --campaign UUID --name "Automated Max Conv" --automated',
13
14
  ];
14
15
  static flags = {
15
16
  ...currencyFlag,
@@ -17,10 +18,14 @@ export default class AsaAdGroupsCreate extends Command {
17
18
  ...pricingModelFlag,
18
19
  ...confirmFlags,
19
20
  ...idempotencyFlags,
21
+ automated: Flags.boolean({
22
+ description: 'Create the automated ad group a Max Conversions campaign needs (no bid, no schedule, always ENABLED)',
23
+ exclusive: ['automated-keywords'],
24
+ }),
20
25
  'automated-keywords': Flags.boolean({ allowNo: true, description: 'Let Apple add keywords automatically' }),
21
26
  campaign: Flags.string({ description: 'Campaign ID (UUID)', required: true }),
22
27
  'cpa-goal': moneyFlag('CPA goal'),
23
- 'default-bid': moneyFlag('Default bid', { required: true }),
28
+ 'default-bid': moneyFlag('Default bid; required unless --automated is set'),
24
29
  name: Flags.string({ description: 'Ad group name', required: true }),
25
30
  status: Flags.string({ description: 'Initial status', options: ['ENABLED', 'PAUSED'] }),
26
31
  };
@@ -28,15 +33,25 @@ export default class AsaAdGroupsCreate extends Command {
28
33
  const { flags } = await this.parse(AsaAdGroupsCreate);
29
34
  if (!isValidUuid(flags.campaign))
30
35
  this.error('Invalid campaign ID format.', { exit: 2 });
36
+ if (!flags.automated && flags['default-bid'] === undefined) {
37
+ this.error('--default-bid is required unless --automated is set.', { exit: 2 });
38
+ }
39
+ if (flags.automated && flags['start-time'] !== undefined) {
40
+ this.error('--start-time is not allowed with --automated: Apple schedules the automated ad group itself.', { exit: 2 });
41
+ }
42
+ if (flags.automated && flags.status === 'PAUSED') {
43
+ this.error('An automated ad group must be ENABLED; pause the campaign instead.', { exit: 2 });
44
+ }
31
45
  const body = {
32
- automated_keywords_opt_in: flags['automated-keywords'],
46
+ automated_keywords_opt_in: flags.automated ? true : flags['automated-keywords'],
47
+ automated_keywords_required: flags.automated ? true : undefined,
33
48
  campaign_id: flags.campaign,
34
49
  cpa_goal: money(flags['cpa-goal'], flags.currency),
35
50
  default_bid_amount: money(flags['default-bid'], flags.currency),
36
51
  end_time: startOfDayUtc(flags['end-time']),
37
52
  name: flags.name,
38
53
  pricing_model: flags['pricing-model'],
39
- start_time: startOfDayUtc(flags['start-time'] ?? todayUtc()),
54
+ start_time: flags.automated ? undefined : startOfDayUtc(flags['start-time'] ?? todayUtc()),
40
55
  status: flags.status,
41
56
  };
42
57
  await confirmMutation(this, { body, method: 'POST', path: '/ad-groups/', summary: `Create ad group ${flags.name}` }, flags.yes);
@@ -17,6 +17,11 @@ export default class AsaCampaignsCreate extends Command {
17
17
  status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
18
  'supply-source': import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
19
19
  'target-cpa': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ 'invoice-advertiser': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ 'invoice-billing-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
+ 'invoice-contact-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
+ 'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
+ 'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
25
  'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
26
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
27
  currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
@@ -1,19 +1,22 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
2
  import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
3
3
  import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
4
- import { currencyFlag, idempotencyFlags, money, moneyFlag } from '../../../lib/asa-flags.js';
4
+ import { currencyFlag, idempotencyFlags, invoiceFlags, locInvoiceDetails, money, moneyFlag, reportServingState, } from '../../../lib/asa-flags.js';
5
5
  import { isValidUuid } from '../../../lib/flags.js';
6
6
  import { printResponse } from '../../../lib/output.js';
7
7
  export default class AsaCampaignsCreate extends Command {
8
- static description = 'Create a campaign in Apple Search Ads';
8
+ static description = 'Create a campaign in Apple Search Ads. A Max Conversions campaign also needs an automated ad group (`asa ad-groups create --automated`); line-of-credit organizations must pass all five --invoice-* flags';
9
9
  static enableJsonFlag = true;
10
10
  static examples = [
11
11
  '<%= config.bin %> asa campaigns create --org UUID --name "Winter push" --adam-id 123456 --country US --daily-budget 50',
12
+ '<%= config.bin %> asa campaigns create --org UUID --name "Max Conv" --adam-id 123456 --country US --daily-budget 50 --bidding-strategy MAX_CONVERSIONS',
13
+ '<%= config.bin %> asa campaigns create --org UUID --name "LOC push" --adam-id 123456 --country US --daily-budget 50 --invoice-advertiser "Acme Inc" --invoice-order-number PO-42 --invoice-contact-name "Jane Doe" --invoice-contact-email jane@acme.com --invoice-billing-email billing@acme.com',
12
14
  ];
13
15
  static flags = {
14
16
  ...currencyFlag,
15
17
  ...confirmFlags,
16
18
  ...idempotencyFlags,
19
+ ...invoiceFlags,
17
20
  'ad-channel-type': Flags.string({ default: 'SEARCH', description: 'Ad channel type', options: ['DISPLAY', 'SEARCH'] }),
18
21
  'adam-id': Flags.integer({ description: 'App Store app ID (adam_id)', required: true }),
19
22
  'bidding-strategy': Flags.string({
@@ -32,12 +35,19 @@ export default class AsaCampaignsCreate extends Command {
32
35
  description: 'Supply source, repeatable',
33
36
  multiple: true,
34
37
  }),
35
- 'target-cpa': moneyFlag('Target CPA'),
38
+ 'target-cpa': moneyFlag('Target CPA; must be lower than --daily-budget'),
36
39
  };
37
40
  async run() {
38
41
  const { flags } = await this.parse(AsaCampaignsCreate);
39
42
  if (!isValidUuid(flags.org))
40
43
  this.error('Invalid org ID format.', { exit: 2 });
44
+ let invoice;
45
+ try {
46
+ invoice = locInvoiceDetails(flags);
47
+ }
48
+ catch (error) {
49
+ this.error(error.message, { exit: 2 });
50
+ }
41
51
  const body = {
42
52
  ad_channel_type: flags['ad-channel-type'],
43
53
  adam_id: flags['adam-id'],
@@ -47,6 +57,7 @@ export default class AsaCampaignsCreate extends Command {
47
57
  campaign_group_id: flags.org,
48
58
  countries_or_regions: flags.country,
49
59
  daily_budget_amount: money(flags['daily-budget'], flags.currency),
60
+ loc_invoice_details: invoice,
50
61
  name: flags.name,
51
62
  status: flags.status,
52
63
  supply_sources: flags['supply-source'],
@@ -62,6 +73,7 @@ export default class AsaCampaignsCreate extends Command {
62
73
  if (result.campaign && !replayed)
63
74
  this.log('Campaign created!');
64
75
  printResponse(result, this.log.bind(this));
76
+ reportServingState(result.campaign, this.warn.bind(this));
65
77
  return result;
66
78
  }
67
79
  }
@@ -15,6 +15,11 @@ export default class AsaCampaignsUpdate extends Command {
15
15
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
16
  status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
17
  'target-cpa': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'invoice-advertiser': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ 'invoice-billing-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ 'invoice-contact-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ 'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
+ 'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
23
  'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
24
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
25
  currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
@@ -1,23 +1,25 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
3
3
  import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
4
- import { currencyFlag, idempotencyFlags, money, moneyFlag } from '../../../lib/asa-flags.js';
4
+ import { currencyFlag, idempotencyFlags, invoiceFlags, locInvoiceDetails, money, moneyFlag, reportServingState, } from '../../../lib/asa-flags.js';
5
5
  import { isValidUuid } from '../../../lib/flags.js';
6
6
  import { printResponse } from '../../../lib/output.js';
7
7
  export default class AsaCampaignsUpdate extends Command {
8
8
  static args = {
9
9
  campaign_id: Args.string({ description: 'Campaign ID (UUID)', required: true }),
10
10
  };
11
- static description = 'Change a campaign: budgets, countries, status or schedule';
11
+ static description = 'Change a campaign: budgets, countries, status, schedule or Invoicing Options (all five --invoice-* flags together; they replace the stored set)';
12
12
  static enableJsonFlag = true;
13
13
  static examples = [
14
14
  '<%= config.bin %> asa campaigns update UUID --status PAUSED',
15
15
  '<%= config.bin %> asa campaigns update UUID --daily-budget 80',
16
+ '<%= config.bin %> asa campaigns update UUID --invoice-advertiser "Acme Inc" --invoice-order-number PO-42 --invoice-contact-name "Jane Doe" --invoice-contact-email jane@acme.com --invoice-billing-email billing@acme.com',
16
17
  ];
17
18
  static flags = {
18
19
  ...currencyFlag,
19
20
  ...confirmFlags,
20
21
  ...idempotencyFlags,
22
+ ...invoiceFlags,
21
23
  'bidding-strategy': Flags.string({
22
24
  description: 'Bidding strategy',
23
25
  options: ['MANUAL_CPT', 'MAX_CONVERSIONS'],
@@ -48,8 +50,16 @@ export default class AsaCampaignsUpdate extends Command {
48
50
  body.target_cpa = money(flags['target-cpa'], flags.currency);
49
51
  if (flags['bidding-strategy'] !== undefined)
50
52
  body.bidding_strategy = flags['bidding-strategy'];
53
+ try {
54
+ const details = locInvoiceDetails(flags);
55
+ if (details)
56
+ body.loc_invoice_details = details;
57
+ }
58
+ catch (error) {
59
+ this.error(error.message, { exit: 2 });
60
+ }
51
61
  if (Object.keys(body).length === 0) {
52
- this.error('Nothing to change. Pass at least one field, e.g. --status PAUSED.', { exit: 2 });
62
+ this.error('Nothing to change. Pass at least one field, e.g. --status PAUSED, or the five --invoice-* flags for a line-of-credit organization.', { exit: 2 });
53
63
  }
54
64
  await confirmMutation(this, { body, method: 'PUT', path: `/campaigns/${args.campaign_id}/`, summary: 'Update campaign' }, flags.yes);
55
65
  const client = await createAsaClient(this.config);
@@ -61,6 +71,7 @@ export default class AsaCampaignsUpdate extends Command {
61
71
  if (result.campaign && !replayed)
62
72
  this.log('Campaign updated!');
63
73
  printResponse(result, this.log.bind(this));
74
+ reportServingState(result.campaign, this.warn.bind(this));
64
75
  return result;
65
76
  }
66
77
  }
@@ -1,5 +1,5 @@
1
1
  import type { QueryParams } from './api-client.js';
2
- import type { AsaMoney, AsaMutationError } from './asa-schemas.js';
2
+ import type { AsaLocInvoiceDetails, AsaMoney, AsaMutationError } from './asa-schemas.js';
3
3
  export declare const MAX_BULK_ITEMS = 100;
4
4
  export declare const MAX_BY_DAYS = 16;
5
5
  export declare const ASA_METRIC_ENTITIES: string[];
@@ -79,6 +79,27 @@ export declare const idempotencyFlags: {
79
79
  'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
80
80
  };
81
81
  export declare function money(amount: string | undefined, currency: string): AsaMoney | undefined;
82
+ declare const INVOICE_FIELDS: {
83
+ readonly 'invoice-advertiser': "client_name";
84
+ readonly 'invoice-billing-email': "billing_contact_email";
85
+ readonly 'invoice-contact-email': "buyer_email";
86
+ readonly 'invoice-contact-name': "buyer_name";
87
+ readonly 'invoice-order-number': "order_number";
88
+ };
89
+ type InvoiceFlagName = keyof typeof INVOICE_FIELDS;
90
+ export declare const invoiceFlags: {
91
+ 'invoice-advertiser': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
92
+ 'invoice-billing-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
93
+ 'invoice-contact-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
94
+ 'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
95
+ 'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
96
+ };
97
+ export declare function locInvoiceDetails(flags: Partial<Record<InvoiceFlagName, string>>): Record<keyof AsaLocInvoiceDetails, string> | undefined;
98
+ export declare function reportServingState(campaign: null | {
99
+ internal_id: string;
100
+ serving_state_reasons?: null | string[];
101
+ serving_status?: null | string;
102
+ }, warn: (msg: string) => void): void;
82
103
  interface BulkOutcome {
83
104
  applied: unknown[];
84
105
  errors: AsaMutationError[];
@@ -124,6 +124,53 @@ export const idempotencyFlags = {
124
124
  export function money(amount, currency) {
125
125
  return amount === undefined ? undefined : { amount, currency };
126
126
  }
127
+ const INVOICE_FIELDS = {
128
+ 'invoice-advertiser': 'client_name',
129
+ 'invoice-billing-email': 'billing_contact_email',
130
+ 'invoice-contact-email': 'buyer_email',
131
+ 'invoice-contact-name': 'buyer_name',
132
+ 'invoice-order-number': 'order_number',
133
+ };
134
+ const INVOICE_PREFIX = 'Invoicing Options (line-of-credit orgs, see payment_model in asa orgs list)';
135
+ function invoiceFlag(what) {
136
+ return Flags.string({ description: `${INVOICE_PREFIX}: ${what}` });
137
+ }
138
+ export const invoiceFlags = {
139
+ 'invoice-advertiser': invoiceFlag('advertiser name'),
140
+ 'invoice-billing-email': invoiceFlag('billing contact email'),
141
+ 'invoice-contact-email': invoiceFlag('buyer contact email'),
142
+ 'invoice-contact-name': invoiceFlag('buyer contact name'),
143
+ 'invoice-order-number': invoiceFlag('order number'),
144
+ };
145
+ export function locInvoiceDetails(flags) {
146
+ const names = Object.keys(INVOICE_FIELDS);
147
+ const missing = names.filter((name) => flags[name] === undefined);
148
+ if (missing.length === names.length)
149
+ return undefined;
150
+ if (missing.length > 0) {
151
+ throw new Error(`Invoicing Options need all five flags; missing: ${missing.map((name) => `--${name}`).join(', ')}`);
152
+ }
153
+ return Object.fromEntries(names.map((name) => [INVOICE_FIELDS[name], flags[name]]));
154
+ }
155
+ const SERVING_HINTS = {
156
+ AUTOMATED_KEYWORDS_REQUIRED_AD_GROUP_MISSING: (campaignId) => `Max Conversions campaign needs an automated ad group: adapty asa ad-groups create --campaign ${campaignId} --name "Automated Max Conv" --automated`,
157
+ MISSING_BO_OR_INVOICING_FIELDS: (campaignId) => `This organization bills by line of credit — add Invoicing Options: adapty asa campaigns update ${campaignId} --invoice-advertiser ... --invoice-order-number ... --invoice-contact-name ... --invoice-contact-email ... --invoice-billing-email ...`,
158
+ };
159
+ export function reportServingState(campaign, warn) {
160
+ if (campaign?.serving_status !== 'NOT_RUNNING' || !campaign.serving_state_reasons?.length)
161
+ return;
162
+ const reasons = campaign.serving_state_reasons;
163
+ if (reasons.length === 1 && reasons[0] === 'PAUSED_BY_USER')
164
+ return;
165
+ for (const reason of reasons) {
166
+ const hint = SERVING_HINTS[reason];
167
+ if (hint)
168
+ warn(hint(campaign.internal_id));
169
+ }
170
+ const other = reasons.filter((reason) => !SERVING_HINTS[reason]);
171
+ if (other.length > 0)
172
+ warn(`Not serving: ${other.join(', ')}`);
173
+ }
127
174
  export function reportBulkOutcome({ applied, errors, isValidationFailure, kind }, log) {
128
175
  if (isValidationFailure) {
129
176
  log(`Nothing was applied: the batch failed validation before Apple was called.`);
@@ -48,6 +48,13 @@ export interface AsaAppDTO {
48
48
  last_synced_at: string;
49
49
  name: string;
50
50
  }
51
+ export interface AsaLocInvoiceDetails {
52
+ billing_contact_email: null | string;
53
+ buyer_email: null | string;
54
+ buyer_name: null | string;
55
+ client_name: null | string;
56
+ order_number: null | string;
57
+ }
51
58
  export interface AsaCampaignGroupDTO {
52
59
  currency: string;
53
60
  internal_id: string;
@@ -55,6 +62,7 @@ export interface AsaCampaignGroupDTO {
55
62
  org_id: number;
56
63
  org_name: string;
57
64
  parent_org_id: number;
65
+ payment_model: null | string;
58
66
  time_zone: string;
59
67
  }
60
68
  export interface AsaCampaignDTO {
@@ -70,8 +78,12 @@ export interface AsaCampaignDTO {
70
78
  daily_budget_amount: AsaMoney | null;
71
79
  end_time: null | string;
72
80
  internal_id: string;
81
+ loc_invoice_details: AsaLocInvoiceDetails | null;
73
82
  name: string;
74
83
  org_id: number;
84
+ payment_model: null | string;
85
+ serving_state_reasons: null | string[];
86
+ serving_status: null | string;
75
87
  start_time: null | string;
76
88
  status: AsaStatus;
77
89
  supply_sources: string[];
@@ -81,6 +93,7 @@ export interface AsaAdGroupDTO {
81
93
  ad_group_id: number;
82
94
  app_id: string;
83
95
  automated_keywords_opt_in: boolean | null;
96
+ automated_keywords_required: boolean | null;
84
97
  bidding_strategy: null | string;
85
98
  campaign_group_id: string;
86
99
  campaign_id: string;
@@ -91,6 +104,8 @@ export interface AsaAdGroupDTO {
91
104
  name: string;
92
105
  payment_model: null | string;
93
106
  pricing_model: null | string;
107
+ serving_state_reasons: null | string[];
108
+ serving_status: null | string;
94
109
  start_time: null | string;
95
110
  status: AsaStatus | null;
96
111
  target_cpa: AsaMoney | null;
@@ -261,7 +276,11 @@ export interface AsaCampaignMutationEntity {
261
276
  campaign_group_id: string;
262
277
  campaign_id: number;
263
278
  internal_id: string;
279
+ loc_invoice_details: AsaLocInvoiceDetails | null;
264
280
  name: string;
281
+ payment_model: null | string;
282
+ serving_state_reasons: null | string[];
283
+ serving_status: null | string;
265
284
  status: AsaStatus;
266
285
  }
267
286
  export interface AsaAdGroupMutationDTO {