Skip to content

Audit and fix space-api TypeSpec migration regressions#32

Merged
Devon-White merged 12 commits into
fern/migrate-typespec-schemasfrom
devin/1768516620-space-api-audit
Jan 20, 2026
Merged

Audit and fix space-api TypeSpec migration regressions#32
Devon-White merged 12 commits into
fern/migrate-typespec-schemasfrom
devin/1768516620-space-api-audit

Conversation

@fern-api

@fern-api fern-api Bot commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

Fixes TypeSpec migration regressions for the Space API including route parameters, nested routes, missing endpoints, and operationIds.

- Fix queues route parameters (queue_id, id) and add missing endpoints
- Fix MFA verify route structure (/{mfa_request_id}/verify)
- Fix registry-beta nested routes for campaigns under brands
- Add missing verified_caller_ids endpoints (update, redial, validate)
- Add missing filter parameters to registry-beta endpoints
- Fix operationIds and summaries to match original spec
- Update TypeSpec to v1.8.0
@fern-api fern-api Bot changed the base branch from main to fern/migrate-typespec-schemas January 15, 2026 22:57
@github-actions

github-actions Bot commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

@cassieemb cassieemb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not finished yet, but need to context switch for a bit and wanted to provide the feedback I have so far!

Comment thread specs/signalwire-rest/space-api/addresses/models/requests.tsp
Comment thread specs/signalwire-rest/space-api/addresses/models/requests.tsp
Comment thread specs/signalwire-rest/space-api/addresses/models/requests.tsp
Comment thread specs/signalwire-rest/space-api/addresses/models/requests.tsp
Comment thread specs/signalwire-rest/space-api/addresses/models/requests.tsp
Comment thread specs/signalwire-rest/space-api/registry-beta/models/requests.tsp
@Devon-White

Copy link
Copy Markdown
Collaborator

@cassieemb regarding the campaign registry endpoints..

Since many request properties become optional if the request is for a self-registered brand, i wonder if we should change how we present this in the spec.

Would a oneOf for two objects, one being for CSP & the other being for regular brands make sense here?

That way we only enforce the required properties where needed (regular brand object).

Comment thread specs/signalwire-rest/space-api/verified-caller-ids/models/requests.tsp Outdated
Comment thread specs/signalwire-rest/space-api/sip-endpoints/models/requests.tsp Outdated
Comment thread specs/signalwire-rest/space-api/campaign-registry/main.tsp
@cassieemb

Copy link
Copy Markdown
Contributor

@cassieemb regarding the campaign registry endpoints..

Since many request properties become optional if the request is for a self-registered brand, i wonder if we should change how we present this in the spec.

Would a oneOf for two objects, one being for CSP & the other being for regular brands make sense here?

That way we only enforce the required properties where needed (regular brand object).

I think that's a really good callout and would be a great way to improve the readability of this endpoint!

@Devon-White

Copy link
Copy Markdown
Collaborator

@cassieemb regarding the campaign registry endpoints..
Since many request properties become optional if the request is for a self-registered brand, i wonder if we should change how we present this in the spec.
Would a oneOf for two objects, one being for CSP & the other being for regular brands make sense here?
That way we only enforce the required properties where needed (regular brand object).

I think that's a really good callout and would be a great way to improve the readability of this endpoint!

Implemented! It seems from the backend code that a lot of these properties that are marked as optional for CSP actually don't get used. So it seems a managed brands request would look like:

{
  "name": "My Brand",
  "company_name": "BrandCo",
  "contact_email": "brand_info@example.com",
  "contact_phone": "+18995551212",
  "ein_issuing_country": "United States",
  "legal_entity_type": "PRIVATE_PROFIT",
  "ein": "12-3456789",
  "company_address": "123 Brand St, Hill Valley CA, 91905",
  "company_vertical": "HEALTHCARE",
  "company_website": "www.example.com",
  "status_callback_url": "https://example.com/handle_callback"
}

While CSPs would look like:

{
  "csp_self_registered": true,
  "name": "My Brand",
  "csp_brand_reference": "B123456",
  "status_callback_url": "https://example.com/handle_callback"
}

So it seems the presence of csp_self_registered is the discriminator, and all the additional brand properties will not be processed if this discriminated property is present and true.

@cassieemb

Copy link
Copy Markdown
Contributor

I'm sorry, I completely missed the campaign mismatches in my first review - I think we may want to structure them similar to the brand creation endpoint.

Create Campaign:
This endpoint, like the brand creation endpoint, has separate requirements based on whether the campaign is managed or partnered. In this case, we determine if the campaign is managed or partnered by evaluating the brand passed in the customer's request.

Both:
name - required, between 3 and 64 characters
brand_id - required, must be undeleted brand in the space
direct_lending - required
embedded_link - required
embedded_phone - required
affiliate_marketing - required
age_gated_content - required
lead_generation - required
status_callback_url - optional

Partner:
csp_campaign_reference - required

Managed:
terms_and_conditions - required
sms_use_case - required
description - required, min 40 characters
sample1 - required, min 20 characters
sample2 - required, min 20 characters
sample3 - optional, min 20 characters if provided
sample4 - optional, min 20 characters if provided
sample5 - optional, min 20 characters if provided
number_pooling_required - required
number_pooling_per_campaign - required if number_pooling_required is true
message_flow - required, min 40 characters
opt_in_message - optional, min 20 characters if provided
opt_out_message - required, min 20 characters
help_message - required, min 20 characters
sub_use_cases - required if the use case is MIXED or LOW_VOLUME_MIXED, but disallowed if the use case is any other kind. if use case is MIXED, list must have between 2 and 5 sub use cases. if LOW_VOLUME_MIXED, list must have between 1 and 5 use cases.
campaign_verify_token - required if use case is 527 (already correct in yours)
dynamic_messages - optional, no length requirement (already correct in yours)
help_keywords - optional, must be comma separated values with no space if provided, does not have a default
opt_in_keywords - optional, must be comma separated values with no space if provided, does not have a default
opt_out_keywords - optional, must be comma separated values with no space if provided, does not have a default

I'm not sure why the UI indicates that there is a default value for the keywords - I checked across the codebase and we don't set any default when nothing is passed. The value is not required by TCR either, so there is no need to - we simply don't pass anything when we create the campaign with TCR. I'll open a PR to fix this in the UI

Let me know if you have any questions about any of these! I can expand further. Everything else looks great IMO

@Devon-White Devon-White merged commit d0f0522 into fern/migrate-typespec-schemas Jan 20, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants