API de envio da Microsoft Store para aplicações MSI ou EXE

Use a API de envio da Microsoft Store para aplicativos MSI ou EXE para consultar e criar envios programaticamente para aplicativos MSI ou EXE para sua conta do Partner Center ou da sua organização. Essa API é útil se sua conta gerencia muitos aplicativos e você deseja automatizar e otimizar o processo de envio para esses ativos. Essa API usa a ID do Microsoft Entra para autenticar as chamadas do seu aplicativo ou serviço.

As etapas a seguir descrevem o processo de ponta a ponta do uso da API de envio da Microsoft Store:

  1. Certifique-se de que preencheu todos os pré-requisitos.
  2. Antes de chamar um método na API de envio da Microsoft Store, obtenha um token de acesso do Microsoft Entra ID. Depois de obter um token, você tem 60 minutos para usá-lo em chamadas para a API de envio da Microsoft Store antes que o token expire. Depois que o token expirar, você poderá gerar um novo token.
  3. Chame a API de submissão da Microsoft Store para aplicações MSI ou EXE.

Etapa 1: concluir os pré-requisitos para usar a API de envio da Microsoft Store

Antes de começar a escrever código para chamar a API de envio da Microsoft Store para o aplicativo MSI ou EXE, verifique se você concluiu os seguintes pré-requisitos.

  • Você (ou sua organização) deve ter um diretório de ID do Microsoft Entra e deve ter permissão de administrador global para o diretório. Se você já usa o Microsoft 365 ou outros serviços empresariais da Microsoft, você já tem o diretório Microsoft Entra ID. Caso contrário, você pode criar uma nova ID do Microsoft Entra no Partner Center sem custo adicional.
  • Você deve associar um aplicativo Microsoft Entra ID à sua conta do Partner Center e obter sua ID de locatário, ID do cliente e chave. Você precisa desses valores para obter um token de acesso do Microsoft Entra ID, que será usado em chamadas para a API de submissão da Microsoft Store.
  • Prepare seu aplicativo para uso com a API de envio da Microsoft Store:
    • Se seu aplicativo ainda não existir no Partner Center, você deverá criá-lo reservando seu nome no Partner Center. Não é possível usar a API de envio da Microsoft Store para criar um aplicativo no Partner Center; você deve trabalhar no Partner Center para criá-lo e, depois disso, você pode usar a API para acessar o aplicativo e criar envios programaticamente para ele.
    • Antes de criar um envio para um determinado aplicativo usando essa API, você deve primeiro criar um envio para o aplicativo no Partner Center, incluindo responder ao questionário de classificações etárias . Depois de fazer isso, você será capaz de criar programaticamente novos envios para este aplicativo usando a API.
    • Se estiver a criar ou atualizar uma submissão de aplicação e precisar incluir um novo pacote, prepare os detalhes do pacote.
    • Se estiver a criar ou atualizar uma submissão de aplicação e precisar incluir capturas de ecrã ou imagens para a listagem da Loja, prepare as capturas de ecrã e as imagens da aplicação.

Como associar um aplicativo Microsoft Entra ID à sua conta do Partner Center

Antes de poder usar a API de envio da Microsoft Store para aplicativos MSI ou EXE, você deve associar um aplicativo Microsoft Entra ID à sua conta do Partner Center, recuperar a ID do locatário e a ID do cliente do aplicativo e gerar uma chave. O aplicativo Microsoft Entra ID representa o aplicativo ou serviço do qual você deseja chamar a API de envio da Microsoft Store. Você precisa da ID do locatário, da ID do cliente e da chave para obter um token de acesso do Microsoft Entra ID que você passa para a API.

Observação

Você só precisa executar essa tarefa uma vez. Depois de ter a ID do locatário, a ID do cliente e a chave, você pode reutilizá-los sempre que precisar criar um novo token de acesso do Microsoft Entra ID.

  1. No Partner Center, associe a conta do Partner Center da sua organização ao diretório Microsoft Entra ID da sua organização.
  2. Em seguida, na página Usuários na seção Configurações de conta do Partner Center, adicione o aplicativo Microsoft Entra ID que representa o aplicativo ou serviço que você usará para acessar envios para sua conta do Partner Center. Certifique-se de atribuir a este aplicativo a função de gerente. Se o aplicativo ainda não existir no diretório do Microsoft Entra ID, você poderá criar um novo aplicativo Microsoft Entra ID no Partner Center.
  3. Retorne à página Usuários, clique no nome do seu aplicativo Microsoft Entra ID para ir para as configurações do aplicativo e copie os valores ID do Locatário e ID do Cliente.
  4. Para adicionar uma nova chave ou segredo de cliente, siga as instruções a seguir ou consulte as instruções para registar a aplicação através do Portal do Azure:

Para registrar seu aplicativo:

  1. Inicie sessão no portal Azure.

  2. Se você tiver acesso a vários locatários, use o filtro Diretórios + assinaturas no menu superior para alternar para o locatário no qual deseja registrar o aplicativo.

  3. Procure e selecione Microsoft Entra ID Directory.

  4. Em Gerir, selecione Registos de > aplicações Selecione a sua aplicação.

  5. Selecione Certificados & segredos Segredos do > cliente Novo segredo do > cliente.

  6. Adicione uma descrição do segredo do cliente.

  7. Selecione uma expiração para o segredo ou especifique um tempo de vida personalizado.

  8. O tempo de vida do segredo do cliente é limitado a dois anos (24 meses) ou menos. Não é possível especificar um tempo de vida personalizado superior a 24 meses.

    Observação

    A Microsoft recomenda que você defina um valor de expiração inferior a 12 meses.

  9. Selecione Adicionar.

  10. Registe o valor do segredo para uso no código da aplicação cliente. Este valor secreto nunca mais é apresentado depois de sair desta página.

Etapa 2: Obter um token de acesso do Microsoft Entra ID

Antes de chamar qualquer um dos métodos na API de envio da Microsoft Store para o aplicativo MSI ou EXE, você deve primeiro obter um token de acesso do Microsoft Entra ID que você passa para o cabeçalho Authorization de cada método na API. Depois de obter um token de acesso, você tem 60 minutos para usá-lo antes que ele expire. Depois que o token expirar, você poderá atualizá-lo para continuar a usá-lo em outras chamadas para a API.

Para obter o token de acesso, siga as instruções em Service to Service Calls Using Client Credentials para enviar um HTTP POST para o endpoint tenant_id/oauth2/token. Aqui está um pedido de amostra.

POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8

grant_type=client_credentials
&client_id=<your_client_id>
&client_secret=<your_client_secret>
&scope=https://api.store.microsoft.com/.default

Para o valor de tenant_id no URI POST e os parâmetros client_id e client_secret, especifique o ID do locatário, o ID do cliente e a chave da sua aplicação que recuperou do Partner Center na seção anterior. Para o parâmetro scope, você deve especificar https://api.store.microsoft.com/.default.

Depois que seu token de acesso expirar, você poderá atualizá-lo seguindo as instruções aqui.

Para obter exemplos que demonstram como obter um token de acesso usando C# ou Node.js, consulte os exemplos de código para API de envio da Microsoft Store para aplicativo MSI ou EXE.

Etapa 3: Usar a API de envio da Microsoft Store

Depois de ter um token de acesso do Microsoft Entra ID, pode-se chamar métodos na API de submissão da Microsoft Store para aplicações MSI ou EXE. A API inclui muitos métodos que são agrupados em cenários para aplicativos. Para criar ou atualizar envios, você normalmente chama vários métodos em uma ordem específica. Para obter informações sobre cada cenário e a sintaxe de cada método, consulte as seguintes seções:

Observação

Depois de obter um token de acesso, tem 60 minutos para chamar métodos na API de submissão da Microsoft Store para um aplicativo MSI ou EXE antes que o token expire.

URL base

A URL base para o aplicativo Microsoft Store Submission API for EXE ou MSI é: https://api.store.microsoft.com

Contratos API

API para Obter Metadados do Envio de Rascunho Atual

Busca metadados em cada módulo (listagens, propriedades ou disponibilidade) no âmbito da atual submissão de rascunho.

Caminho [Todos os módulos]: /submission/v1/product/{productId}/metadata?languages={languages}&includelanguagelist={true/false}
Caminho [Módulo único]: /submission/v1/product/{productId}/metadata/{moduleName}?languages={languages}&includelanguagelist={true/false}
Método: GET

parâmetros de caminho

Parâmetro Descrição
productId O ID do Centro de Parceiros do produto
nome_do_módulo Módulo Partner Center – anúncios, propriedades ou disponibilidade

Parâmetros de Consulta

Parâmetro Descrição
Idiomas Opcional Os idiomas da listagem filtram como uma cadeia de caracteres separada por vírgulas [limite de até 200 idiomas].

Se ausente, os primeiros 200 metadados de idiomas de listagem disponíveis são recuperados. [ por exemplo, "en-us, en-gb"].
IncluirLista de Idiomas Opcional Booleano – se verdadeiro, retorna a lista de idiomas de listagem adicionados e seu status de completude.

cabeçalhos necessários

Cabeçalho Valor
Authorization: Bearer <Token> A ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

cabeçalhos de resposta

Cabeçalho Valor
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
suporte de acessibilidade booleano
Termos Adicionais de Licença Cordão
disponibilidade Objeto Dados do módulo de disponibilidade
categoria Cordão Veja a lista de categorias abaixo
Notas de Certificação Cordão
código Cordão O código de erro da mensagem
informações de contacto Cordão
Copyright Cordão
depende de drivers ou NT booleano
descrição Cordão
desenvolvidoPor Cordão
capacidade de descoberta Cordão [DESCUBRÍVEL, DEEPLINK_ONLY]
habilitarInFutureMarkets booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
Versão experimental gratuita Cordão [SEM_PERÍODO_DE_EXPERIÊNCIA_GRATUITO, PERÍODO_DE_EXPERIÊNCIA_GRATUITO]
hardwareItemType Cordão
éPoliticaDePrivacidadeNecessária booleano
éRecomendado booleano
é obrigatório booleano
éSucesso booleano
éFuncionalidadeDoSistemaObrigatória Matriz de objetos
linguagem Cordão Ver lista de línguas abaixo
Anúncios Matriz de objetos Dados do módulo de listagens para cada idioma
mercados Matriz de cadeias de caracteres Veja a lista de mercados abaixo
mensagem Cordão A descrição do erro
Hardware mínimo Cordão
mínimoRequisito Cordão
suporte para caneta e tinta digital booleano
preços Cordão [GRÁTIS, FREEMIUM, ASSINATURA, PAGO]
URL da Política de Privacidade Cordão
declarações de produto Objeto
características do produto Matriz de cadeias de caracteres
propriedades Objeto Dados do módulo de propriedades
Hardware recomendado Cordão
Requisito Recomendado Cordão
dadosDeResposta Objeto Contém a carga útil de resposta real para a solicitação
Requerimentos Matriz de objetos
termos de pesquisa Matriz de cadeias de caracteres
breveDescrição Cordão
Subcategoria Cordão Ver lista de subcategorias abaixo
suporteContactInfo Cordão
DetalhesDosRequisitosDoSistema Matriz de objetos
destino Cordão A entidade da qual o erro se originou
site Cordão
O que há de novo Cordão

Atualizar a API de metadados de envio de rascunho atual

Atualiza metadados em cada módulo em fase de rascunho. A API verifica

  • Para Submissão Ativa. Se existir, falha com mensagem de erro.
  • Se todos os módulos estiverem no estado pronto para permitir a operação de salvar rascunho.
  • Cada campo na submissão é validado de acordo com os requisitos da Loja
  • Requisitos do sistema Regras de validação de detalhes:
    • Valores permitidos em hardwareItemType = Memória: 300MB, 750MB, 1GB, 2GB, 4GB, 6GB, 8GB, 12GB, 16GB, 20GB
    • Valores permitidos em hardwareItemType = DirectX: DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12
    • Valores permitidos em hardwareItemType = Video_Memory: 1GB, 2GB, 4GB, 6GB

Path [Atualização completa do módulo]: /submission/v1/product/{productId}/metadata
Método: PUT

Path [Atualização do patch do módulo]: /submission/v1/product/{productId}/metadata
Método: PATCH

Comportamento da API

No caso da API de Atualização do Módulo Completo – os Dados do Módulo inteiro precisam estar presentes na Solicitação de atualização completa de cada campo. Qualquer campo que não esteja presente em Solicitação, seu valor padrão é usado para substituir o valor atual para esse módulo específico.
No caso da API de Atualização do Módulo de Patch – apenas os campos que devem ser atualizados precisam estar presentes na Solicitação. Esses valores de campo de Solicitação substituirão seus valores existentes, mantendo todos os outros campos que não estão presentes na Solicitação, iguais aos atuais para esse Módulo específico.

parâmetros de caminho

Parâmetro Descrição
productId O ID do Centro de Parceiros do produto

cabeçalhos necessários

Cabeçalho Valor
Authorization: Bearer <Token> A ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

Parâmetros de solicitação

Nome Tipo Descrição
disponibilidade Objeto Objeto para armazenar metadados do Módulo de Disponibilidade
mercados Matriz de cadeias de caracteres Necessário Veja a lista de mercados abaixo
capacidade de descoberta Cordão Necessário [DETETÁVEL, DEEPLINK_ONLY]
habilitarInFutureMarkets booleano Obrigatório
preços Cordão Obrigatório [GRÁTIS, FREEMIUM, ASSINATURA, PAGO]
Versão experimental gratuita Cordão Obrigatório se o preço for PAGO ou SUBSCRIÇÃO [NO_FREE_TRIAL, FREE_TRIAL]
propriedades Objeto Objeto para armazenar metadados do módulo de propriedades
éPoliticaDePrivacidadeNecessária booleano Obrigatório
URL da Política de Privacidade Cordão Obrigatório se isPrivacyPolicyRequired = true Deve ser um URL válido
site Cordão Deve ser um URL válido
suporteContactInfo Cordão Deve ser um URL ou endereço de e-mail válido
Notas de Certificação Cordão Recomendado Limite de caracteres = 2000
categoria Cordão Necessário Veja a lista de categorias abaixo
Subcategoria Cordão Necessário Ver lista de subcategorias abaixo
declarações de produto Objeto Obrigatório
éFuncionalidadeDoSistemaObrigatória Matriz de objetos [Toque, teclado, mouse, câmera, NFC_HCE, NFC_Proximity, Bluetooth_LE, telefonia, microfone]
é obrigatório booleano Obrigatório
éRecomendado booleano Obrigatório
hardwareItemType Cordão Obrigatório
DetalhesDosRequisitosDoSistema Matriz de objetos [Processador, gráficos, memória, DirectX Video_Memory]
mínimoRequisito Cordão necessário para systemRequirementsText, MaxLength = 200

Valores permitidos em hardwareItemType = Memória: [300MB, 750MB, 1GB, 2GB, 4GB, 6GB, 8GB, 12GB, 16GB, 20GB]

Valores permitidos em hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Valores permitidos em hardwareItemType = Video_Memory: [1GB, 2GB, 4GB, 6GB]
Requisito Recomendado Cordão necessário para systemRequirementsText, MaxLength = 200

Valores permitidos em hardwareItemType = Memória: [300MB, 750MB, 1GB, 2GB, 4GB, 6GB, 8GB, 12GB, 16GB, 20GB]

Valores permitidos em hardwareItemType = DirectX: [DX9, DX10, DX11, DX12-FEATURELEVEL11, DX12-FEATURELEVEL12]

Valores permitidos em hardwareItemType = Video_Memory: [1GB, 2GB, 4GB, 6GB]
depende de drivers ou NT booleano Obrigatório
suporte de acessibilidade booleano Obrigatório
suporte para caneta e tinta digital booleano Obrigatório
Anúncios Objeto Opor-se à listagem de dados do módulo para um único idioma
linguagem Cordão Necessário Ver lista de línguas abaixo
descrição Cordão Necessário Limite de caracteres = 10000
O que há de novo Cordão Limite de caracteres = 1500
características do produto Matriz de String 200 caracteres por recurso; Até 20 funcionalidades
breveDescrição Cordão Limite de caracteres = 1000
termos de pesquisa Matriz de String 30 caracteres por termo de pesquisa; Até 7 termos de pesquisa

21 palavras únicas no total em todos os termos de pesquisa
Termos Adicionais de Licença Cordão Necessário Limite de caracteres = 10000
Copyright Cordão Limite de caracteres = 200
desenvolvidoPor Cordão Limite de caracteres = 255
Requerimentos Matriz de objetos 200 caracteres por item; Até 11 itens TOTAL entre mínimo e recomendado]
Hardware mínimo Cordão Limite de caracteres = 200
Hardware recomendado Cordão Limite de caracteres = 200
informações de contacto Cordão Limite de caracteres = 200
anúncios para adicionar Matriz de cadeias de caracteres Ver lista de línguas abaixo
listagensParaRemover Matriz de cadeias de caracteres Ver lista de línguas abaixo

Mercados

Ver tabela | Mercado | Abreviatura | |--------|--------------| | Afeganistão | AF | | Albânia | AL | | Argélia | DZ | | Samoa Americana | AS | | Andorra | AD | | Angola | AO | | Anguila | AI | | Antártida | AQ | | Antígua e Barbuda | AG | | Argentina | AR | | Arménia | AM | | Aruba | AW | | Austrália | AU | | Áustria | AT | | Azerbaijão | AZ | | Bahamas | BS | | Bahrein | BH | | Bangladeche | BD | | Barbados | BB | | Bielorrússia | BY | | Bélgica | BE | | Belize | BZ | | Benim | BJ | | Bermudas | BM | | Butão | BT | | Venezuela | VE | | Bolívia | BO | | Bonaire | BQ | | Bósnia e Herzegovina | BA | | Botsuana | BW | | Ilha Bouvet | BV | | Brasil | BR | | Território Britânico do Oceano Índico | IO | | Ilhas Virgens Britânicas | VG | | Brunei | BN | | Bulgária | BG | | Burkina Faso | BF | | Burundi | BI | | Camboja | KH | | Camarões | CM | | Canadá | CA | | Cabo Verde | CV | | Ilhas Caimão | KY | | República Centro-Africana | CF | | Chade | TD | | Chile | CL | | China | CN | | Ilha Christmas | CX | | Ilhas Cocos (Keeling) | CC | | Colômbia | CO | | Comores | KM | | Congo | CG | | Congo (RDC) | CD | | Ilhas Cook | CK | | Costa Rica | CR | | Croácia | HR | | Curaçau | CW | | Chipre | CY | | República Checa | CZ | | Costa do Marfim | CI | | Dinamarca | DK | | Djibuti | DJ | | Dominica | DM | | República Dominicana | DO | | Equador | EC | | Egito | EG | | El Salvador | SV | | Guiné Equatorial | GQ | | Eritreia | ER | | Estónia | EE | | Etiópia | ET | | Ilhas Malvinas | FK | | Ilhas Faroé | FO | | Fiji | FJ | | Finlândia | FI | | França | FR | | Guiana Francesa | GF | | Polinésia Francesa | PF | | Terras Austrais e Antárticas Francesas | TF | | Gabão | GA | | Gâmbia | GM | | Geórgia | GE | | Alemanha | DE | | Gana | GH | | Gibraltar | GI | | Grécia | GR | | Gronelândia | GL | | Granada | GD | | Guadalupe | GP | | Guam | GU | | Guatemala | GT | | Guernsey | GG | | Guiné | GN | | Guiné-Bissau | GW | | Guiana | GY | | Haiti | HT | | Ilha Heard e Ilhas McDonald | HM | | Cidade do Vaticano | VA | | Honduras | HN | | Região Administrativa Especial de Hong Kong | HK | | Hungria | HU | | Islândia | IS | | Índia | IN | | Indonésia | ID | | Iraque | IQ | | Irlanda | IE | | Israel | IL | | Itália | IT | | Jamaica | JM | | Japão | JP | | Jersey | JE | | Jordânia | JO | | Cazaquistão | KZ | | Quénia | KE | | Kiribati | KI | | Coreia | KR | | Koweit | KW | | Quirguizistão | KG | | Laos | LA | | Letónia | LV | | Líbano | LB | | Lesoto | LS | | Libéria | LR | | Líbia | LY | | Liechtenstein | LI | | Lituânia | LT | | Luxemburgo | LU | | Macau | MO | | Macedónia do Norte | MK | | Madagáscar | MG | | Maláui | MW | | Malásia | MY | | Maldivas | MV | | Mali | ML | | Malta | MT | | Ilha de Man | IM | | Ilhas Marshall | MH | | Martinica | MQ | | Mauritânia | MR | | Maurícia | MU | | Maiote | YT | | México | MX | | Micronésia | FM | | Moldávia | MD | | Mónaco | MC | | Mongólia | MN | | Montenegro | ME | | Montserrat | MS | | Marrocos | MA | | Moçambique | MZ | | Mianmar | MM | | Namíbia | NA | | Nauru | NR | | Nepal | NP | | Países Baixos | NL | | Nova Caledónia | NC | | Nova Zelândia | NZ | | Nicarágua | NI | | Níger | NE | | Nigéria | NG | | Niue | NU | | Ilha de Norfolk | NF | | Ilhas Marianas do Norte | MP | | Noruega | NO | | Omã | OM | | Paquistão | PK | | Palau | PW | | Autoridade Palestiniana | PS | | Panamá | PA | | Papua-Nova Guiné | PG | | Paraguai | PY | | Peru | PE | | Filipinas | PH | | Ilhas Pitcairn | PN | | Polónia | PL | | Portugal | PT | | Catar | QA | | Reunião | RE | | Roménia | RO | | Rússia | RU | | Ruanda | RW | | São Bartolomeu | BL | | Santa Helena, Ascensão e Tristão da Cunha | SH | | São Cristóvão e Neves | KN | | Santa Lúcia | LC | | São Martinho (parte francesa) | MF | | São Pedro e Miquelon | PM | | São Vicente e Granadinas | VC | | Samoa | WS | | São Marinho | SM | | Arábia Saudita | SA | | Senegal | SN | | Sérvia | RS | | Seicheles | SC | | Serra Leoa | SL | | Singapura | SG | | São Martinho (parte holandesa) | SX | | Eslováquia | SK | | Eslovénia | SI | | Ilhas Salomão | SB | | Somália | SO | | África do Sul | ZA | | Geórgia do Sul e Ilhas Sandwich do Sul | GS | | Espanha | ES | | Sri Lanka | LK | | Suriname | SR | | Svalbard e Jan Mayen | SJ | | Suazilândia | SZ | | Suécia | SE | | Suíça | CH | | São Tomé e Príncipe | ST | | Taiwan | TW | | Tajiquistão | TJ | | Tanzânia | TZ | | Tailândia | TH | | Timor-Leste | TL | | Togo | TG | | Tokelau | TK | | Tonga | TO | | Trindade e Tobago | TT | | Tunísia | TN | | Turquia | TR | | Turquemenistão | TM | | Ilhas Turcas e Caicos | TC | | Tuvalu | TV | | Ilhas Menores Distantes dos EUA | UM | | Ilhas Virgens Americanas | VI | | Uganda | UG | | Ucrânia | UA | | Emirados Árabes Unidos | AE | | Reino Unido | GB | | Estados Unidos | US | | Uruguai | UY | | Usbequistão | UZ | | Vanuatu | VU | | Vietname | VN | | Wallis e Futuna | WF | | Iémen | YE | | Zâmbia | ZM | | Zimbabué | ZW | | Ilhas Åland | AX |

Categorias e subcategorias

Mostrar tabela | Categoria | Subcategorias | |-----------------------|---------------| | Livros e Referência | EReader, Ficção, Não Ficção, Referência | | Negócios | Contabilidade e Finanças, Colaboração, CRM, Dados e Análise, Gestão de Ficheiros, Inventário e Logística, Legal e Recursos Humanos, Gestão de Projetos, Desktop Remoto, Vendas e Marketing, Tempo e Despesas | | Ferramentas de Desenvolvimento | Base de Dados, Ferramentas de Design, Kits de Desenvolvimento, Redes, Referência e Formação, Servidores, Utilitários, Alojamento Web | Educação | Livros e Referência Educativa, Aprendizagem Inicial, Ferramentas de Instrução, Língua, Auxílios de Estudo | | Entretenimento | (Nenhum) | | Alimentação e Refeições | (Nenhum) | | Governo e Política | (Nenhum) | | Saúde e Fitness | (Nenhum) | | Crianças e Família | Livros e Referência para Crianças e Família, Entretenimento para Crianças e Família, Hobbies e Brinquedos, Desportos e Atividades, Viagens para Crianças e Família | | Estilo de Vida | Automotivo, DIY, Casa e Jardim, Relacionamentos, Interesse Especial, Estilo e Moda | | Medicina | (Nenhum) | | Design Multimedia | Ilustração e Design Gráfico, Produção Musical, Produção de Foto e Vídeo | | Música | (Nenhum) | | Navegação e Mapas | (Nenhum) | | Notícias e Meteorologia | Notícias, Meteorologia | | Finanças Pessoais | Banca e Investimentos, Orçamentação e Impostos | | Personalização | Toques e Sons, temas, Papel de Parede e Ecrãs de Bloqueio | | Foto e Vídeo | (Nenhum) | | Produtividade | (Nenhum) | | Segurança | Proteção de PC, Segurança Pessoal | | Compras | (Nenhum) | | Social | (Nenhum) | | Desporto | (Nenhum) | | Viagens | Guias de Cidade, Hotéis | | Ferramentas e Utilitários | Cópia de Segurança e Gestão, Gestor de Ficheiros |

Idiomas

Ver tabela | Nome da língua | Códigos de idioma suportados | |---------------|--------------------------| | Africâner | af, af-za | | Albanês | sq, sq-al | | Amárico | am, am-et | | Arménio | hy, hy-am | | Assamese | as, as-in | | Azerbaijano | az-arab, az-arab-az, az-cyrl, az-cyrl-az, az-latn, az-latn-az | | Basco (Basco) | eu, eu-es | | Belarusso | be, be-by | | Bangla | bn, bn-bd, bn-in | | Bósnio | bs, bs-cyrl, bs-cyrl-ba, bs-latn, bs-latn-ba | | Búlgaro | bg, bg-bg | | Catalão | ca, ca-es, ca-es-valencia | | Cherokee | chr-cher, chr-cher-us, chr-latn | | Chinês (simplificado) | zh-Hans, zh-cn, zh-hans-cn, zh-sg, zh-hans-sg | | Chinês (Tradicional) | zh-Hant, zh-hk, zh-mo, zh-tw, zh-hant-hk, zh-hant-mo, zh-hant-tw, zh-mo, zh-tw, zh-hant-hk, zh-hant-mo, zh-hant-tw | | Croata | hr, hr-hr, hr-ba | | Checo | cs, cs-cz | | Dinamarquês | da, da-dk | | Dari | prs, prs-af, prs-arab | | Holandês | nl, nl-nl, nl-be | | Inglês | en, en-au, en-ca, en-gb, en-ie, en-in, en-nz, en-sg, en-us, en-za, en-bz, en-hk, en-id, en-jm, en-kz, en-mt, en-my, en-ph, en-pk, en-tt, en-vn, en-zw | | Estónio | et, et-ee | | Filipino | fil, fil-latn, fil-ph | | Finlandês | fi, fi-fi | | Francês | fr, fr-be , fr-ca , fr-ch , fr-fr , fr-lu, fr-cd, frc-latn, frp-latn | | Galego | gl, gl-es | | Georgiano | ka, ka-ge | | Alemão | de, de-at, de-ch, de-de, de-lu, de-li | | Grego | el, el-gr | | Gujarati | gu, gu-in | | Hausa | ha, ha-latn, ha-latn-ng | | Hebraico | he, he-il | | Hindi | hi, hi-in | | Húngaro | hu, hu-hu | | Islandês | is, is-is | | Igbo | ig-latn, ig-ng | | Indonésio | id, id-id | | Inuktitut (Latim) | iu-cans, iu-latn, iu-latn-ca | | Irlandês | ga, ga-ie | | isiXhosa | xh, xh-za | | isiZulu | zu, zu-za | | Italiano | it, it-it, it-ch | | Japonês | ja, ja-jp | | Canarês | kn, kn-in | | Cazaque | kk, kk-kz | | Khmer | km, km-kh | | K'iche' | quc-latn, qut-gt, qut-latn | | Kinyarwanda | rw, rw-rw | | KiSwahili | sw, sw-ke | | Konkani | kok, kok-in | | Coreano | ko, ko-kr | | Curdo | ku-arab, ku-arab-iq | | Quirguiz | ky-kg, ky-cyrl | | Laosiano | lo, lo-la | | Letão | lv, lv-lv | | Lituano | lt, lt-lt | | Luxemburguês | lb, lb-lu | | Macedónio | mk, mk-mk | | Malaio | ms, ms-bn, ms-my | | Malaiala | ml, ml-in | | Maltês | mt, mt-mt | | Maori | mi, mi-latn, mi-nz | | Marathi | mr, mr-in | | Mongol (Cirílico) | mn-cyrl, mn-mong, mn-mn, mn-phag | | Nepalês | ne, ne-np | | Norueguês | nb, nb-no, nn, nn-no, no, no-no | | Odia | or, or-in | | Persa | fa, fa-ir | | Polaco | pl, pl-pl | | Português (Brasil) | pt-br | | Português (Portugal) | pt, pt-pt | | Punjabi | pa, pa-arab, pa-arab-pk, pa-deva, pa-in | | Quéchua | quz, quz-bo, quz-ec, quz-pe | | Romeno | ro, ro-ro | | Russo | ru , ru-ru | | Gaélico Escocês | gd-gb, gd-latn | | Sérvio (Latim) | sr-Latn, sr-latn-cs, sr, sr-latn-ba, sr-latn-me, sr-latn-rs | | Sérvio (Cirílico) | sr-cyrl, sr-cyrl-ba, sr-cyrl-cs, sr-cyrl-me, sr-cyrl-rs | | Sesotho sa Leboa | nso, nso-za | | Setsuana | tn, tn-bw, tn-za | | Sindhi | sd-arab, sd-arab-pk, sd-deva | | Cingalês | si, si-lk | | Eslovaco | sk, sk-sk | | Esloveno | sl, sl-si | | Espanhol | es, es-cl, es-co, es-es, es-mx, es-ar, es-bo, es-cr, es-do, es-ec, es-gt, es-hn, es-ni, es-pa, es-pe, es-pr, es-py, es-sv, es-us, es-uy, es-ve | | Sueco | sv, sv-se, sv-fi | | Tajique (Cirílico) | tg-arab, tg-cyrl, tg-cyrl-tj, tg-latn | | Tâmil | ta, ta-in | | Tártaro | tt-arab, tt-cyrl, tt-latn, tt-ru | | Telugu | te, te-in | | Tailandês | th, th-th | | Tigrínia | ti, ti-et | | Turco | tr, tr-tr | | Turcomeno | tk-cyrl, tk-latn, tk-tm, tk-latn-tr, tk-cyrl-tr | | Ucraniano | uk, uk-ua | | Urdu | ur, ur-pk | | Uigur | ug-arab, ug-cn, ug-cyrl, ug-latn | | Uzbeque (Latim) | uz, uz-cyrl, uz-latn, uz-latn-uz | | Vietnamita | vi, vi-vn | | Galês | cy, cy-gb | | Wolof | wo, wo-sn | | Iorubá | yo-latn, yo-ng |

Pedido de amostra

{
    "availability":{
        "markets": ["US"],
        "discoverability": "DISCOVERABLE",
        "enableInFutureMarkets": true,
        "pricing": "PAID",
        "freeTrial": "NO_FREE_TRIAL"
    },
    "properties":{
        "isPrivacyPolicyRequired": true,
        "privacyPolicyUrl": "http://contoso.com",
        "website": "http://contoso.com",
        "supportContactInfo": "http://contoso.com",
        "certificationNotes": "Certification Notes",
        "category": "DeveloperTools",
        "subcategory": "Database",
        "productDeclarations": {
            "dependsOnDriversOrNT": false,
            "accessibilitySupport": false,
            "penAndInkSupport": false
        },
        "isSystemFeatureRequired": [
        {
            "isRequired": true,
                "isRecommended": false,
                "hardwareItemType": "Touch"
            },
            {
                "isRequired": true,
                "isRecommended": false,
                "hardwareItemType": "Keyboard"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Mouse"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Camera"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "NFC_HCE"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "NFC_Proximity"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Bluetooth_LE"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Telephony"
            },
            {
                "isRequired": false,
                "isRecommended": false,
                "hardwareItemType": "Microphone"
            }
        ],
        "systemRequirementDetails": [
            {
                "minimumRequirement": "1GB",
                "recommendedRequirement": "4GB",
                "hardwareItemType": "Memory"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "DirectX"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "Video_Memory"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "Processor"
            },
            {
                "minimumRequirement": "",
                "recommendedRequirement": "",
                "hardwareItemType": "Graphics"
            }
        ]
    },
    "listings":{
        "language": "en-us",
        "description": "Description",
        "whatsNew": "What's New",
        "productFeatures": ["Feature 1"],
        "shortDescription": "Short Description",
        "searchTerms": ["Search Ter 1"],
        "additionalLicenseTerms": "License Terms",
        "copyright": "Copyright Information",
        "developedBy": "Developer Details",
        "sortTitle": "Product 101",
        "requirements": [
            {
                "minimumHardware": "Pentium4",
                "recommendedHardware": "Corei9"
            }
        ],
        "contactInfo": "contactus@contoso.com"               
    },      
    "listingsToAdd": ["en-au"],
    "listingsToRemove": ["en-gb"]
}

cabeçalhos de resposta

Cabeçalho Valor
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto Contém a carga útil de resposta real para a solicitação
URL de sondagem Cordão URL de consulta para obter o estado de qualquer submissão In-Progress
idSubmissaoEmAndamento Cordão ID de qualquer submissão já do tipo In-Progress

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    } 
}

Obter API de pacotes de rascunho atuais

Busca detalhes do pacote no envio de rascunho atual.

Path [Todos os pacotes]: /submission/v1/product/{productId}/packages
Método: GET

Caminho [Pacote único]: /submission/v1/product/{productId}/packages/{packageId}
Método: GET

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto
packageId O ID exclusivo do pacote a ser buscado

cabeçalhos necessários

Cabeçalho Valor
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

cabeçalhos de resposta

Cabeçalho Valor
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de erros ou mensagens de aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
Pacotes Matriz de objetos Objeto para armazenar dados do módulo do pacote
packageId Cordão
URL do Pacote Cordão
Idiomas Matriz de cadeias de caracteres
Arquiteturas Matriz de cadeias de caracteres [Neutro, x86, x64, Arm, Arm64]
isSilentInstall booleano Isso deve ser marcado como verdadeiro se o instalador for executado no modo silencioso sem exigir parâmetros ou então falso.
parâmetros do instalador Cordão
genéricoDocUrl Cordão
detalhes do erro Matriz de objetos
Cenário de Erro Cordão
detalhesDoCenárioDeErro Matriz de objetos
errorValue Cordão
errorUrl Cordão
Tipo de pacote Cordão

Exemplo de resposta

{   
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
    }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData":{
        "packages":[{
            "packageId": "pack0832",
            "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
            "languages": ["en-us"],
            "architectures": ["X86"],
            "isSilentInstall": true,
            "installerParameters": "/s",
            "genericDocUrl": "https://docs.contoso.com/doclink",
            "errorDetails": [{
                "errorScenario": "rebootRequired",
                "errorScenarioDetails": [{
                    "errorValue": "ERR001001",
                    "errorUrl": "https://errors.contoso.com/errors/ERR001001"
                }]
            }],
            "packageType": "exe",
        }]
    }
}

Atualizar a API de Pacotes de Rascunhos Atuais

Atualiza os detalhes do pacote nesta submissão de rascunho atual.

Path [Atualização completa do módulo]: /submission/v1/product/{productId}/packages
Método: PUT

Path [Atualização de patch de pacote único]: /submission/v1/product/{productId}/packages/{packageId}
Método: PATCH

Comportamento da API

No caso da API de Atualização de Módulo Completo – os dados de pacotes inteiros precisam estar presentes na solicitação de atualização completa de cada campo. Qualquer campo que não esteja presente na solicitação, seu valor padrão é usado para substituir o valor atual para esse módulo específico. Isso resulta na substituição de todos os pacotes existentes por um novo conjunto de pacotes da solicitação. Isso resultará na regeneração de IDs de Pacote e o usuário deverá chamar a API de Pacotes GET para obter as Ids de Pacote mais recentes.

No caso da API de Atualização de Patch de Pacote Único – apenas os campos que devem ser atualizados para um determinado pacote precisam estar presentes na solicitação. Esses valores de campo da solicitação substituirão seus valores existentes, mantendo todos os outros campos que não estão presentes na solicitação, iguais aos atuais para esse pacote específico. Outros pacotes no conjunto permanecem como estão.

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto
packageId A ID exclusiva do pacote

cabeçalhos necessários

Cabeçalho Valor
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

Parâmetros de solicitação

Nome Tipo Descrição
Pacotes Matriz de objetos Objeto para armazenar dados do módulo de pacote [Apenas necessário para atualização completa do módulo]
URL do Pacote Cordão Obrigatório
Idiomas Matriz de cadeias de caracteres Obrigatório
Arquiteturas Matriz de cadeias de caracteres Necessário Deve conter uma única arquitetura - Neutro, X86, X64, Arm, Arm64
isSilentInstall booleano Obrigatório Isto deve ser marcado como verdadeiro se o instalador for executado no modo silencioso sem exigir parâmetros, caso contrário, falso.
parâmetros do instalador Cordão Obrigatório se isSilentInstall for false
genéricoDocUrl Cordão Necessário se packageType for exe Link para documento que contém detalhes de códigos de erro personalizados para o instalador do tipo EXE
detalhes do erro Matriz de objetos Metadados para armazenar códigos de erro personalizados e detalhes para instaladores do tipo EXE.
Cenário de Erro Cordão Identifique o cenário de erro específico. [instalaçãoCanceladaPeloUtilizador, aplicaçãoJáExiste, instalaçãoJáEmProgresso, espaçoEmDiscoCheio, reinícioNecessário, falhaDeRede, pacoteRejeitadoDuranteInstalação, instalaçãoBemSucedida, diversos]
detalhesDoCenárioDeErro Matriz de objetos
errorValue Cordão Código de erro que pode estar presente durante a instalação
errorUrl Cordão URL para ter detalhes sobre o erro
Tipo de pacote Cordão Necessário [exe, msi]

Solicitação de amostra [Atualização completa do módulo]

{
    "packages":[{
        "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
        "languages": ["en-us"],
        "architectures": ["X86"],
        "isSilentInstall": true,
        "installerParameters": "/s",
        "genericDocUrl": "https://docs.contoso.com/doclink",
        "errorDetails": [{
            "errorScenario": "rebootRequired",
            "errorScenarioDetails": [{
                "errorValue": "ERR001001",
                "errorUrl": "https://errors.contoso.com/errors/ERR001001"
            }]
        }],
        "packageType": "exe",
    }]
}

Solicitação de exemplo [Atualização de patch de pacote único]

{
    "packageUrl": "https://www.contoso.com/downloads/1.1/setup.exe",
    "languages": ["en-us"],
    "architectures": ["X86"],
    "isSilentInstall": true,
    "installerParameters": "/s",
    "genericDocUrl": "https://docs.contoso.com/doclink",
    "errorDetails": [{
        "errorScenario": "rebootRequired",
        "errorScenarioDetails": [{
            "errorValue": "ERR001001",
            "errorUrl": "https://errors.contoso.com/errors/ERR001001"
        }]
    }],
    "packageType": "exe",
}

cabeçalhos de resposta

Cabeçalho Valor
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos [A lista de mensagens de erro ou aviso, se houver]
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A Entidade da qual o erro se originou
dadosDeResposta Objeto
URL de sondagem Cordão [URL para consulta para obter o estado de submissão em caso de qualquer envio existente já com In-Progress]
idSubmissaoEmAndamento Cordão [ID de submissão de qualquer submissão já In-Progress]

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    } 
}

API de Consolidação de Pacotes

Confirma o novo conjunto de Pacotes atualizados utilizando as APIs de Atualização de Pacotes na submissão de rascunho atual. Esta API devolve um URL de sondagem para acompanhar o upload do pacote.

Caminho: /submission/v1/product/{productId}/packages/commit
Método: POST

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto

cabeçalhos necessários

Cabeçalho Valor
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

cabeçalhos de resposta

Cabeçalho Valor
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos [A lista de mensagens de erro ou aviso, se houver]
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
URL de sondagem Cordão [Consulta de URL para obter o status de Upload de Pacote ou Status de Submissão no caso de qualquer Envio Anterior In-Progress]
idSubmissaoEmAndamento Cordão [ID de qualquer submissão que já seja In-Progress]

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/status",
        "ongoingSubmissionId": ""
    } 
}

Obter a API dos Ativos da Listagem de Rascunho Atual

Busca detalhes de ativos de listagem no envio de rascunho atual.

Caminho: /submission/v1/product/{productId}/listings/assets?languages={languages}
Método: GET

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto

Parâmetros de Consulta

Nome Descrição
Idiomas [Opcional] O filtro de idiomas listados como lista separada por vírgulas [limite de até 200 idiomas]. Se não existirem, os primeiros 200 dados de ativos do idioma de listagem disponíveis são recuperados. (por exemplo, "en-us, en-gb")

cabeçalhos necessários

Cabeçalho Valor
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

cabeçalhos de resposta

Cabeçalho Valor
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
listagem de Ativos Matriz de objetos Listando detalhes de ativos para cada idioma
linguagem Cordão
logótipos de loja Matriz de objetos
capturas de ecrã Matriz de objetos
ID Cordão
URL do ativo Cordão Deve ser um URL válido
tamanho da imagem Objeto
Largura Número inteiro
altura Número inteiro

Exemplo de resposta

{   
"isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData":{
        "listingAssets": [{
            "language": "en-us",
            "storeLogos": [
                {
                    "id": "1234567890abcdefgh",
                    "assetUrl": "https://contoso.com/blob=1234567890abcdefgh",
                    "imageSize": {
                        "width": 2160,
                        "height": 2160
                    }
                }
            ],
            "screenshots": [
                {
                    "id": "1234567891abcdefgh",
                    "assetUrl": "https://contoso.com/blob=1234567891abcdefgh",
                    "imageSize": {
                        "width": 2160,
                        "height": 2160
                    }
                }
            ]
        }]
    }
}

Criar API de listagem de ativos

Cria um novo Carregamento de Recursos de Listagem na submissão de rascunho atual.

Atualização de ativos de listagem

A API de Submissão da Microsoft Store para aplicações EXE ou MSI utiliza URLs SAS geradas em tempo de execução para Repositórios de Blobs em cada upload de ativo de imagem individual, juntamente com uma chamada à API de Commit após a conclusão bem-sucedida do upload. Para ter a capacidade de atualizar ativos de listagem e, por sua vez, para poder adicionar/remover localidades no módulo de listagem, a seguinte abordagem pode ser usada:

  1. Utilize a API Criar Listagem de Ativos para enviar pedido referente ao carregamento de ativos, com o idioma, o tipo e a contagem de ativos.
  2. Com base no número de ativos solicitados, os IDs de ativos são criados sob demanda e criariam uma URL SAS de curto prazo e a enviariam de volta no Corpo de Resposta sob o tipo de ativos. Você pode usar este URL para carregar ativos de imagem de tipo específico usando clientes HTTP [Colocar Blob (API REST) - Armazenamento do Azure | Microsoft Docs].
  3. Depois de carregar, você pode usar a API Confirmar Listagem de Ativos para também enviar as novas informações de ID de Ativo recebidas anteriormente da chamada de API anterior. A API única confirmará internamente os dados de listagem de ativos após a validação.
  4. Essa abordagem substituirá efetivamente todo o conjunto de Imagens anteriores do Tipo de Ativo em Idioma específico que está sendo enviado em Solicitação. Assim, os Ativos carregados anteriormente serão removidos.

Caminho: /submission/v1/product/{productId}/listings/assets/create
Método: POST

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto

cabeçalhos necessários

Cabeçalho Descrição
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

Parâmetros de solicitação

Nome Tipo Descrição
linguagem Cordão Obrigatório
createAssetRequest Objeto Obrigatório
Captura de ecrã Número inteiro Necessário se o ISV precisar atualizar capturas de tela ou adicionar novo idioma de listagem [1 - 10]
Logótipo Número inteiro Obrigatório se o ISV precisar atualizar logotipos ou adicionar novo idioma de listagem [1 ou 2]

cabeçalhos de resposta

Cabeçalho Descrição
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
listagem de Ativos Objeto Objeto contendo detalhes dos logotipos da loja e das capturas de ecrã a serem carregados
linguagem Cordão
logótipos de loja Matriz de objetos
capturas de ecrã Matriz de objetos
ID Cordão
URL de Carregamento de Ativo Principal Cordão URL principal para transferir um recurso de listagem usando a API REST do Azure Blob
secundárioAtivoCarregarURL Cordão URL secundária para carregar recurso de listagem usando a API REST do Blob do Azure
Método http Método HTTP O método HTTP precisava ser usado para carregar ativos por meio das URLs de carregamento de ativos – primária ou secundária
Cabeçalhos HTTP Objeto Um objeto com chaves como cabeçalhos obrigatórios para estar presente na chamada da API de carregamento para URLs de envio de ativos. Se o valor não estiver vazio, os cabeçalhos precisarão ter valores específicos. Caso contrário, os valores são calculados durante a chamada de API.

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "listingAssets": {
            "language": "en-us",
            "storeLogos":[{
                "id": "1234567890abcdefgh",
                "primaryAssetUploadUrl": "https://contoso.com/upload?blob=1234567890abcdefgh&sig=12345",
                "secondaryAssetUploadUrl": "https://contoso.com/upload?blob=0987654321abcdfger&sig=54326",
                "httpMethod": "PUT",
                "httpHeaders": {"Required Header Name": "Header Value"}
            }],
            "screenshots":[{
                "id": "0987654321abcdfger",
                "primaryAssetUploadUrl": "https://contoso.com/upload?blob=0987654321abcdfger&sig=54321",
                "secondaryAssetUploadUrl": "https://contoso.com/upload?blob=0987654321abcdfger&sig=54322",
                "httpMethod": "PUT",
                "httpHeaders": {"Required Header Name": "Header Value"}

            }]
        }
    } 
}

API de Comprometimento de Listagem de Ativos

Confirma o novo Ativo de Listagem carregado usando os detalhes da API Criar Ativos na submissão de rascunho atual.

Caminho: /submission/v1/product/{productId}/listings/assets/commit
Método: PUT

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto

cabeçalhos necessários

Cabeçalho Descrição
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

Parâmetros de solicitação

Nome Tipo Descrição
listagem de Ativos Objeto
linguagem Cordão
logótipos de loja Matriz de objeto
capturas de ecrã Matriz de objeto
ID Cordão Deve ser uma ID existente que o usuário deseja persistir da API Get Current Listing Assets ou uma nova ID sob a qual um novo Asset foi carregado na Create Listing Assets API.
URL do ativo Cordão Deve ser a URL do ativo existente que o usuário deseja persistir da API Get Current Listing Assets ou a URL de upload – primária ou secundária, usando a qual um novo ativo foi carregado na API Create Listing Assets. Deve ser um URL válido

Pedido de amostra

{
    "listingAssets": { 
        "language": "en-us",    
        "storeLogos": [
            {
                "id": "1234567890abcdefgh",
                "assetUrl": "https://contoso.com/blob=1234567890abcdefgh",
            }
        ],
        "screenshots": [
            {
                "id": "1234567891abcdefgh",
                "assetUrl": "https://contoso.com/blob=1234567891abcdefgh",
            }
        ]
    }
}

cabeçalhos de resposta

Cabeçalho Descrição
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
URL de sondagem Cordão URL de sondagem para obter o status de qualquer envio em andamento
idSubmissaoEmAndamento Cordão ID de submissão de qualquer submissão já em curso

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    } 
}

API de sondagem de status do módulo

API para verificar a prontidão do módulo antes que o envio possa ser criado. Também valida o status de carregamento do pacote.

Caminho: /submission/v1/product/{productId}/status
Método: GET

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto

cabeçalhos necessários

Cabeçalho Descrição
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

cabeçalhos de resposta

Cabeçalho Descrição
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
estáPronto booleano Indica se todos os módulos estão no estado pronto, incluindo o carregamento do pacote
idSubmissaoEmAndamento Cordão ID de submissão de qualquer submissão já em curso

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "isReady": true,
        "ongoingSubmissionId": ""
    }
}

Criar API de envio

Cria uma submissão a partir do rascunho atual para uma aplicação MSI ou EXE. A API verifica:

  • para envio ativo e falha com mensagem de erro se existir um envio ativo.
  • se todos os módulos estiverem no estado pronto para criar submissão.
  • cada campo na submissão é validado de acordo com os requisitos da Loja

Caminho:/submission/v1/product/{productId}/submit
Método: POST

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto

cabeçalhos necessários

Cabeçalho Descrição
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

cabeçalhos de resposta

Cabeçalho Descrição
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
URL de sondagem Cordão URL de consulta para obter o estado de preparação do módulo, incluindo o carregamento do pacote para submissão
ID da submissão Cordão O ID da submissão recém-criada
idSubmissaoEmAndamento Cordão ID de submissão de qualquer submissão já em curso

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "submissionId": "1234567890", 
        "pollingUrl": "/submission/v1/product/{productId}/submission/{submissionId}/status",
        "ongoingSubmissionId": ""
    }
}

API de pesquisa de status de envio

API para verificar o status de envio.

Caminho: /submission/v1/product/{productId}/submission/{submissionId}/status
Método: GET

parâmetros de caminho

Nome Descrição
productId O ID do Centro de Parceiros do produto

cabeçalhos necessários

Cabeçalho Descrição
Authorization: Bearer <Token> Usando a ID do aplicativo Microsoft Entra ID registrada com a conta do Partner Center
X-Seller-Account-Id ID do vendedor da conta do Partner Center

cabeçalhos de resposta

Cabeçalho Descrição
X-Correlation-ID O ID único do tipo GUID para cada pedido. Isso pode ser compartilhado com a equipe de suporte para analisar qualquer problema.
Retry-After O tempo, em segundos, que o cliente precisa esperar antes de chamar as APIs novamente devido ao limite de taxa.

Parâmetros de resposta

Nome Tipo Descrição
éSucesso booleano
erros Matriz de objetos A lista de mensagens de erro ou aviso, se houver
código Cordão O código de erro da mensagem
mensagem Cordão A descrição do erro
destino Cordão A entidade da qual o erro se originou
dadosDeResposta Objeto
estado de publicação Cordão Status de publicação do envio - [EM PROGRESSO, PUBLICADO, FALHOU, DESCONHECIDO]
hasFailed booleano Indica se a publicação falhou e não será repetida

Exemplo de resposta

{
    "isSuccess": true,
    "errors": [{
        "code": "badrequest",
        "message": "Error Message 1",
        "target": "listings"
        }, {
        "code": "warning",
        "message": "Warning Message 1",
        "target": "properties"
    }],
    "responseData": {
        "publishingStatus": "INPROGRESS",
        "hasFailed": false
    }
}

Exemplos de código

Os artigos a seguir fornecem exemplos de código detalhados que demonstram como usar a API de envio da Microsoft Store em diferentes linguagens de programação:

Exemplo de C#: API de envio da Microsoft Store para aplicativo MSI ou EXE

Este artigo fornece exemplos de código C# que demonstram como usar a API de envio da Microsoft Store para aplicativos MSI ou EXE. Você pode revisar cada exemplo para saber mais sobre a tarefa que ele demonstra ou pode criar todos os exemplos de código neste artigo em um aplicativo de console.

Pré-requisitos Estes exemplos usam a seguinte biblioteca:

  • Newtonsoft.Json pacote NuGet da Newtonsoft.

Programa principal O exemplo a seguir implementa um programa de linha de comando que chama os outros métodos de exemplo neste artigo para demonstrar diferentes maneiras de usar a API de envio da Microsoft Store. Para adaptar este programa para seu próprio uso:

  • Atribua a propriedade SellerId à ID do vendedor da sua conta do Partner Center.
  • Atribua a propriedade ApplicationId à ID do aplicativo que você deseja gerenciar.
  • Atribua as propriedades ClientId e ClientSecret ao ID do cliente e à chave da sua aplicação e substitua o string tenantid na URL TokenEndpoint pelo ID do locatário da sua aplicação. Para obter mais informações, consulte Como associar um aplicativo Microsoft Entra ID à sua conta do Partner Center
using System;
using System.Threading.Tasks;

namespace Win32SubmissionApiCSharpSample
{
    public class Program
    {
        static async Task Main(string[] args)
        {
            var config = new ClientConfiguration()
            {
                ApplicationId = "...",
                ClientId = "...",
                ClientSecret = "...",
                Scope = "https://api.store.microsoft.com/.default",
                ServiceUrl = "https://api.store.microsoft.com",
                TokenEndpoint = "...",
                SellerId = 0
            };

            await new AppSubmissionUpdateSample(config).RunAppSubmissionUpdateSample();

        }
    }
}

Classe auxiliar ClientConfiguration usando C#

O aplicativo de exemplo usa a classe auxiliar ClientConfiguration para passar dados do Microsoft Entra ID Directory e dados do aplicativo para cada um dos métodos de exemplo que usam a API de envio da Microsoft Store.

using System;
using System.Collections.Generic;
using System.Text;

namespace Win32SubmissionApiCSharpSample
{
    public class ClientConfiguration
    {
        /// <summary>
        /// Client Id of your Microsoft Entra ID Directory app.
        /// Example" 00001111-aaaa-2222-bbbb-3333cccc4444
        /// </summary>
        public string ClientId { get; set; }

        /// <summary>
        /// Client secret of your Microsoft Entra ID Directory app
        /// </summary>
        public string ClientSecret { get; set; }

        /// <summary>
        /// Service root endpoint.
        /// Example: "https://api.store.microsoft.com"
        /// </summary>
        public string ServiceUrl { get; set; }

        /// <summary>
        /// Token endpoint to which the request is to be made. Specific to your Microsoft Entra ID Directory app
        /// Example: https://login.microsoftonline.com/d454d300-128e-2d81-334a-27d9b2baf002/oauth2/v2.0/token
        /// </summary>
        public string TokenEndpoint { get; set; }

        /// <summary>
        /// Resource scope. If not provided (set to null), default one is used for the production API
        /// endpoint ("https://api.store.microsoft.com/.default")
        /// </summary>
        public string Scope { get; set; }

        /// <summary>
        /// Partner Center Application ID.
        /// Example: 3e31a9f9-84e8-4d2d-9eba-487878d02ebf
        /// </summary>
        public string ApplicationId { get; set; }


        /// <summary>
        /// The Partner Center Seller Id
        /// Example: 123456892
        /// </summary>
        public int SellerId { get; set; }
    }
}

Criar um envio de aplicativo usando C#

O exemplo a seguir implementa uma classe que usa vários métodos na API de envio da Microsoft Store para atualizar um envio de aplicativo.

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Win32SubmissionApiCSharpSample
{
    public class AppSubmissionUpdateSample
    {
        private ClientConfiguration ClientConfig;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="configuration">An instance of ClientConfiguration that contains all parameters populated</param>
        public AppSubmissionUpdateSample(ClientConfiguration configuration)
        {
            this.ClientConfig = configuration;
        }

        /// <summary>
        /// Main method to Run the Sample Application
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public async Task RunAppSubmissionUpdateSample()
        {
            // **********************
            //       SETTINGS
            // **********************
            var appId = this.ClientConfig.ApplicationId;
            var clientId = this.ClientConfig.ClientId;
            var clientSecret = this.ClientConfig.ClientSecret;
            var serviceEndpoint = this.ClientConfig.ServiceUrl;
            var tokenEndpoint = this.ClientConfig.TokenEndpoint;
            var scope = this.ClientConfig.Scope;

            // Get authorization token.
            Console.WriteLine("Getting authorization token");
            var accessToken = await SubmissionClient.GetClientCredentialAccessToken(
                tokenEndpoint,
                clientId,
                clientSecret,
                scope);

            var client = new SubmissionClient(accessToken, serviceEndpoint);

            client.DefaultHeaders = new Dictionary<string, string>()
            {
                {"X-Seller-Account-Id", this.ClientConfig.SellerId.ToString() }
            };

            Console.WriteLine("Getting Current Application Draft Status");
            
            dynamic AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                SubmissionClient.Version, appId), null);
            
            Console.WriteLine(AppDraftStatus.ToString());

            Console.WriteLine("Getting Application Packages ");

            dynamic PackagesResponse = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.PackagesUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(PackagesResponse.ToString());

            Console.WriteLine("Getting Single Package");

            dynamic SinglePackageResponse = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.PackageByIdUrlTemplate,
                SubmissionClient.Version, appId, (string)PackagesResponse.responseData.packages[0].packageId), null);

            Console.WriteLine(SinglePackageResponse.ToString());

            Console.WriteLine("Updating Entire Package Set");

            // Update data in Packages list to have final set of updated Packages

            // Example - Updating Installer Parameters
            PackagesResponse.responseData.packages[0].installerParameters = "/s /r new-args";

            dynamic PackagesUpdateRequest = new
            {
                packages = PackagesResponse.responseData.packages
            };

            dynamic PackagesUpdateResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.PackagesUrlTemplate,
                SubmissionClient.Version, appId), PackagesUpdateRequest);

            Console.WriteLine(PackagesUpdateResponse.ToString());

            Console.WriteLine("Updating Single Package's Download Url");

            // Update data in the SinglePackage object

            var SinglePackageUpdateRequest = SinglePackageResponse.responseData.packages[0];

            // Example - Updating Installer Parameters
            SinglePackageUpdateRequest.installerParameters = "/s /r /t new-args";

            dynamic PackageUpdateResponse = await client.Invoke<dynamic>(HttpMethod.Patch, string.Format(SubmissionClient.PackageByIdUrlTemplate,
                SubmissionClient.Version, appId, SinglePackageUpdateRequest.packageId), SinglePackageUpdateRequest);

            Console.WriteLine("Committing Packages");

            dynamic PackageCommitResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.PackagesCommitUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(PackageCommitResponse.ToString());

            Console.WriteLine("Polling Package Upload Status");

            AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                SubmissionClient.Version, appId), null);

            while (!((bool)AppDraftStatus.responseData.isReady))
            {
                AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                    SubmissionClient.Version, appId), null);

                Console.WriteLine("Waiting for Upload to finish");

                await Task.Delay(TimeSpan.FromSeconds(2));

                if(AppDraftStatus.errors != null && AppDraftStatus.errors.Count > 0)
                {
                    for(var index = 0; index < AppDraftStatus.errors.Count; index++)
                    {
                        if(AppDraftStatus.errors[index].code == "packageuploaderror")
                        {
                            throw new InvalidOperationException("Package Upload Failed. Please try committing packages again.");
                        }
                    }
                }
            }

            Console.WriteLine("Getting Application Metadata - All Modules");

            dynamic AppMetadata = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.AppMetadataUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppMetadata.ToString());

            Console.WriteLine("Getting Application Metadata - Listings");

            dynamic AppListingsMetadata = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.AppListingsFetchMetadataUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppListingsMetadata.ToString());

            Console.WriteLine("Updating Listings Metadata - Description");

            // Update Required Fields in Listings Metadata Object - Per Language. For eg. AppListingsMetadata.responseData.listings[0]

            // Example - Updating Description
            AppListingsMetadata.responseData.listings[0].description = "New Description Updated By C# Sample Code";

            dynamic ListingsUpdateRequest = new
            {
                listings = AppListingsMetadata.responseData.listings[0]
            };

            dynamic UpdateListingsMetadataResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.AppMetadataUrlTemplate,
                SubmissionClient.Version, appId), ListingsUpdateRequest);

            Console.WriteLine(UpdateListingsMetadataResponse.ToString());

            Console.WriteLine("Getting All Listings Assets");

            dynamic ListingAssets = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ListingAssetsUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(ListingAssets.ToString());

            Console.WriteLine("Creating Listing Assets for 1 Screenshot");

            
            dynamic AssetCreateRequest = new
            {
                language = ListingAssets.responseData.listingAssets[0].language,
                createAssetRequest = new Dictionary<string, int>()
                {
                    {"Screenshot", 1 },
                    {"Logo", 0 }
                }
            };

            dynamic AssetCreateResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.ListingAssetsCreateUrlTemplate,
               SubmissionClient.Version, appId), AssetCreateRequest);

            Console.WriteLine(AssetCreateResponse.ToString());

            Console.WriteLine("Uploading Listing Assets");

            // Path to PNG File to be Uploaded as Screenshot / Logo
            var PathToFile = "./Image.png";
            var AssetToUpload = File.OpenRead(PathToFile);

            await client.UploadAsset(AssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl.Value as string, AssetToUpload);

            Console.WriteLine("Committing Listing Assets");

            dynamic AssetCommitRequest = new
            {
                listingAssets = new
                {
                    language = ListingAssets.responseData.listingAssets[0].language,
                    storeLogos = ListingAssets.responseData.listingAssets[0].storeLogos,
                    screenshots = JToken.FromObject(new List<dynamic>() { new
                {
                    id = AssetCreateResponse.responseData.listingAssets.screenshots[0].id.Value as string,
                    assetUrl = AssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl.Value as string
                }
                }.ToArray())
                }
            };

            dynamic AssetCommitResponse = await client.Invoke<dynamic>(HttpMethod.Put, string.Format(SubmissionClient.ListingAssetsCommitUrlTemplate,
               SubmissionClient.Version, appId), AssetCommitRequest);

            Console.WriteLine(AssetCommitResponse.ToString());

            Console.WriteLine("Getting Current Application Draft Status before Submission");

            AppDraftStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.ProductDraftStatusPollingUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(AppDraftStatus.ToString());

            if (AppDraftStatus == null || !((bool)AppDraftStatus.responseData.isReady))
            {
                throw new InvalidOperationException("Application Current Status is not in Ready Status for All Modules");
            }

            Console.WriteLine("Creating Submission");

            dynamic SubmissionCreationResponse = await client.Invoke<dynamic>(HttpMethod.Post, string.Format(SubmissionClient.CreateSubmissionUrlTemplate,
                SubmissionClient.Version, appId), null);

            Console.WriteLine(SubmissionCreationResponse.ToString());

            Console.WriteLine("Current Submission Status");

            dynamic SubmissionStatus = await client.Invoke<dynamic>(HttpMethod.Get, string.Format(SubmissionClient.SubmissionStatusPollingUrlTemplate,
                SubmissionClient.Version, appId, SubmissionCreationResponse.responseData.submissionId.Value as string), null);

            Console.Write(SubmissionStatus.ToString());

            // User can Poll on this API to know if Submission Status is INPROGRESS, PUBLISHED or FAILED.
            // This Process involves File Scanning, App Certification and Publishing and can take more than a day.
        }
    }
}

IngestionClient classe auxiliar usando C#

A classe IngestionClient fornece métodos auxiliares que são usados por outros métodos no aplicativo de exemplo para executar as seguintes tarefas:

  • Obtenha um token de acesso do Microsoft Entra ID que pode ser usado para invocar métodos na API de submissão da Microsoft Store. Depois de obter um token, você tem 60 minutos para usá-lo em chamadas para a API de envio da Microsoft Store antes que o token expire. Depois que o token expirar, você poderá gerar um novo token.
  • Processe as solicitações HTTP para a API de envio da Microsoft Store.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Win32SubmissionApiCSharpSample
{
    /// <summary>
    /// This class is a proxy that abstracts the functionality of the API service
    /// </summary>
    public class SubmissionClient : IDisposable
    {
        public static readonly string Version = "1";
        private HttpClient httpClient;
        private HttpClient imageUploadClient;

        private readonly string accessToken;

        public static readonly string PackagesUrlTemplate = "/submission/v{0}/product/{1}/packages";
        public static readonly string PackageByIdUrlTemplate = "/submission/v{0}/product/{1}/packages/{2}";
        public static readonly string PackagesCommitUrlTemplate = "/submission/v{0}/product/{1}/packages/commit";
        public static readonly string AppMetadataUrlTemplate = "/submission/v{0}/product/{1}/metadata";
        public static readonly string AppListingsFetchMetadataUrlTemplate = "/submission/v{0}/product/{1}/metadata/listings";
        public static readonly string ListingAssetsUrlTemplate = "/submission/v{0}/product/{1}/listings/assets";
        public static readonly string ListingAssetsCreateUrlTemplate = "/submission/v{0}/product/{1}/listings/assets/create";
        public static readonly string ListingAssetsCommitUrlTemplate = "/submission/v{0}/product/{1}/listings/assets/commit";
        public static readonly string ProductDraftStatusPollingUrlTemplate = "/submission/v{0}/product/{1}/status";
        public static readonly string CreateSubmissionUrlTemplate = "/submission/v{0}/product/{1}/submit";
        public static readonly string SubmissionStatusPollingUrlTemplate = "/submission/v{0}/product/{1}/submission/{2}/status";

        public const string JsonContentType = "application/json";
        public const string PngContentType = "image/png";
        public const string BinaryStreamContentType = "application/octet-stream";

        /// <summary>
        /// Initializes a new instance of the <see cref="SubmissionClient" /> class.
        /// </summary>
        /// <param name="accessToken">
        /// The access token. This is JWT a token obtained from Microsoft Entra ID Directory allowing the caller to invoke the API
        /// on behalf of a user
        /// </param>
        /// <param name="serviceUrl">The service URL.</param>
        public SubmissionClient(string accessToken, string serviceUrl)
        {
            if (string.IsNullOrEmpty(accessToken))
            {
                throw new ArgumentNullException("accessToken");
            }

            if (string.IsNullOrEmpty(serviceUrl))
            {
                throw new ArgumentNullException("serviceUrl");
            }

            this.accessToken = accessToken;
            this.httpClient = new HttpClient
            {
                BaseAddress = new Uri(serviceUrl)
            };
            this.imageUploadClient = new HttpClient();
            this.DefaultHeaders = new Dictionary<string, string>();
        }

        /// <summary>
        /// Gets or Sets the default headers.
        /// </summary>
        public Dictionary<string, string> DefaultHeaders { get; set; }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting
        /// unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this.httpClient != null)
            {
                this.httpClient.Dispose();
                this.httpClient = null;
                GC.SuppressFinalize(this);
            }
        }

        /// <summary>
        /// Gets the authorization token for the provided client id, client secret, and the scope.
        /// This token is usually valid for 1 hour, so if your submission takes longer than that to complete,
        /// make sure to get a new one periodically.
        /// </summary>
        /// <param name="tokenEndpoint">Token endpoint to which the request is to be made. Specific to your
        /// Microsoft Entra ID Directory app. Example: https://login.microsoftonline.com/d454d300-128e-2d81-334a-27d9b2baf002/oauth2/v2.0/token </param>
        /// <param name="clientId">Client Id of your Microsoft Entra ID Directory app. Example" 00001111-aaaa-2222-bbbb-3333cccc4444</param>
        /// <param name="clientSecret">Client secret of your Microsoft Entra ID Directory app</param>
        /// <param name="scope">Scope. If not provided, default one is used for the production API endpoint.</param>
        /// <returns>Autorization token. Prepend it with "Bearer: " and pass it in the request header as the
        /// value for "Authorization: " header.</returns>
        public static async Task<string> GetClientCredentialAccessToken(
            string tokenEndpoint,
            string clientId,
            string clientSecret,
            string scope = null)
        {
            if (scope == null)
            {
                scope = "https://api.store.microsoft.com/.default";
            }

            dynamic result;
            using (HttpClient client = new HttpClient())
            {
                string tokenUrl = tokenEndpoint;
                using (
                    HttpRequestMessage request = new HttpRequestMessage(
                        HttpMethod.Post,
                        tokenUrl))
                {
                    string strContent =
                        string.Format(
                            "grant_type=client_credentials&client_id={0}&client_secret={1}&scope={2}",
                            clientId,
                            clientSecret,
                            scope);

                    request.Content = new StringContent(strContent, Encoding.UTF8,
                        "application/x-www-form-urlencoded");

                    using (HttpResponseMessage response = await client.SendAsync(request))
                    {
                        string responseContent = await response.Content.ReadAsStringAsync();
                        result = JsonConvert.DeserializeObject(responseContent);
                    }
                }
            }

            return result.access_token;
        }


        /// <summary>
        /// Invokes the specified HTTP method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="httpMethod">The HTTP method.</param>
        /// <param name="relativeUrl">The relative URL.</param>
        /// <param name="requestContent">Content of the request.</param>
        /// <returns>instance of the type T</returns>
        /// <exception cref="ServiceException"></exception>
        public async Task<T> Invoke<T>(HttpMethod httpMethod,
            string relativeUrl,
            object requestContent)
        {
            using (var request = new HttpRequestMessage(httpMethod, relativeUrl))
            {
                this.SetRequest(request, requestContent);

                using (HttpResponseMessage response = await this.httpClient.SendAsync(request))
                {
                    T result;
                    if (this.TryHandleResponse(response, out result))
                    {
                        return result;
                    }

                    if (response.IsSuccessStatusCode)
                    {
                        var resource = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
                        return resource;
                    }

                    throw new Exception(await response.Content.ReadAsStringAsync());
                }
            }
        }

        /// <summary>
        /// Uploads a given Image Asset file to Asset Storage
        /// </summary>
        /// <param name="assetUploadUrl">Asset Storage Url</param>
        /// <param name="fileStream">The Stream instance of file to be uploaded</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task UploadAsset(string assetUploadUrl, Stream fileStream)
        {
            using (var request = new HttpRequestMessage(HttpMethod.Put, assetUploadUrl))
            {
                request.Headers.Add("x-ms-blob-type", "BlockBlob");
                request.Content = new StreamContent(fileStream);
                request.Content.Headers.ContentType = new MediaTypeHeaderValue(PngContentType);
                using (HttpResponseMessage response = await this.imageUploadClient.SendAsync(request))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        return;
                    }
                    throw new Exception(await response.Content.ReadAsStringAsync());
                }
            }
        }

        /// <summary>
        /// Sets the request.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="requestContent">Content of the request.</param>
        protected virtual void SetRequest(HttpRequestMessage request, object requestContent)
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.accessToken);

            foreach (var header in this.DefaultHeaders)
            {
                request.Headers.Add(header.Key, header.Value);
            }

            if (requestContent != null)
            {
                request.Content = new StringContent(JsonConvert.SerializeObject(requestContent),
                        Encoding.UTF8,
                        JsonContentType);
                
            }
        }


        /// <summary>
        /// Tries the handle response.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="response">The response.</param>
        /// <param name="result">The result.</param>
        /// <returns>true if the response was handled</returns>
        protected virtual bool TryHandleResponse<T>(HttpResponseMessage response, out T result)
        {
            result = default(T);
            return false;
        }
    }
}

Node.js exemplo: API de envio da Microsoft Store para aplicativo MSI ou EXE

Este artigo fornece exemplos de código Node.js que demonstram como usar a API de submissão da Microsoft Store para aplicações MSI ou EXE. Você pode revisar cada exemplo para saber mais sobre a tarefa que ele demonstra ou pode criar todos os exemplos de código neste artigo em um aplicativo de console.

Pré-requisitos Estes exemplos usam a seguinte biblioteca:

  • node-fetch v2 [npm instalar node-fetch@2]

Criar um envio de aplicativo usando node.js

O exemplo a seguir invoca os outros métodos exemplificados neste artigo para demonstrar diferentes maneiras de usar a API de submissão da Microsoft Store. Para adaptar este programa para seu próprio uso:

  • Atribua a propriedade SellerId à ID do vendedor da sua conta do Partner Center.
  • Atribua a propriedade ApplicationId à ID do aplicativo que você deseja gerenciar.
  • Atribua as propriedades ClientId e ClientSecret ao ID do cliente e à chave da sua aplicação e substitua o string tenantid na URL TokenEndpoint pelo ID do locatário da sua aplicação. Para obter mais informações, consulte Como associar um aplicativo Microsoft Entra ID à sua conta do Partner Center

O exemplo a seguir implementa uma classe que usa vários métodos na API de envio da Microsoft Store para atualizar um envio de aplicativo.

const config = require('./Configuration');
const submissionClient = require('./SubmissionClient');
const fs = require('fs');

var client = new submissionClient(config);

/**
 * Main entry method to Run the Store Submission API Node.js Sample
 */
async function RunNodeJsSample(){
    print('Getting Access Token');
    await client.getAccessToken();
    
    print('Getting Current Application Draft Status');
    var currentDraftStatus = await client.callStoreAPI(client.productDraftStatusPollingUrlTemplate, 'get');
    print(currentDraftStatus);

    print('Getting Application Packages');
    var currentPackages = await client.callStoreAPI(client.packagesUrlTemplate, 'get');
    print(currentPackages);

    print('Getting Single Package');
    var packageId = currentPackages.responseData.packages[0].packageId;
    var packageIdUrl = `${client.packageByIdUrlTemplate}`.replace('{packageId}', packageId);
    var singlePackage = await client.callStoreAPI(packageIdUrl, 'get');
    print(singlePackage);

    print('Updating Entire Package Set');
    // Update data in Packages list to have final set of updated Packages
    currentPackages.responseData.packages[0].installerParameters = "/s /r new-args";
    var packagesUpdateRequest = {
        'packages': currentPackages.responseData.packages
    };
    print(packagesUpdateRequest);
    var packagesUpdateResponse = await client.callStoreAPI(client.packagesUrlTemplate, 'put', packagesUpdateRequest);
    print(packagesUpdateResponse);

    print('Updating Single Package\'s Download Url');
    // Update data in the SinglePackage object
    singlePackage.responseData.packages[0].installerParameters = "/s /r /t new-args";
    var singlePackageUpdateResponse = await client.callStoreAPI(packageIdUrl, 'patch', singlePackage.responseData.packages[0]);
    print(singlePackageUpdateResponse);

    print('Committing Packages');
    var commitPackagesResponse = await client.callStoreAPI(client.packagesCommitUrlTemplate, 'post');
    print(commitPackagesResponse);

    await poll(async ()=>{
        print('Waiting for Upload to finish');
        return await client.callStoreAPI(client.productDraftStatusPollingUrlTemplate, 'get');
    }, 2);

    print('Getting Application Metadata - All Modules');
    var appMetadata = await client.callStoreAPI(client.appMetadataUrlTemplate, 'get');
    print(appMetadata);

    print('Getting Application Metadata - Listings');
    var appListingMetadata = await client.callStoreAPI(client.appListingsFetchMetadataUrlTemplate, 'get');
    print(appListingMetadata);

    print('Updating Listings Metadata - Description');   
    // Update Required Fields in Listings Metadata Object - Per Language. For eg. AppListingsMetadata.responseData.listings[0]
    // Example - Updating Description
    appListingMetadata.responseData.listings[0].description = 'New Description Updated By Node.js Sample Code';
    var listingsUpdateRequest = {
        'listings': appListingMetadata.responseData.listings[0]
    };
    var listingsMetadataUpdateResponse = await client.callStoreAPI(client.appMetadataUrlTemplate, 'put', listingsUpdateRequest);
    print(listingsMetadataUpdateResponse);

    print('Getting All Listings Assets');
    var listingAssets = await client.callStoreAPI(client.listingAssetsUrlTemplate, 'get');
    print(listingAssets);

    print('Creating Listing Assets for 1 Screenshot');
    var listingAssetCreateRequest = {
        'language': listingAssets.responseData.listingAssets[0].language,
        'createAssetRequest': {
            'Screenshot': 1,
            'Logo': 0
        }
    };
    var listingAssetCreateResponse = await client.callStoreAPI(client.listingAssetsCreateUrlTemplate, 'post', listingAssetCreateRequest);
    print(listingAssetCreateResponse);

    print('Uploading Listing Assets');
    const pathToFile = './Image.png';
    const stats = fs.statSync(pathToFile);
    const fileSize = stats.size;
    const fileStream = fs.createReadStream(pathToFile);
    await client.uploadAssets(listingAssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl, fileStream, fileSize);

    print('Committing Listing Assets');
    var assetCommitRequest = {
        'listingAssets': {
            'language': listingAssets.responseData.listingAssets[0].language,
            'storeLogos': listingAssets.responseData.listingAssets[0].storeLogos,
            'screenshots': [{
                'id': listingAssetCreateResponse.responseData.listingAssets.screenshots[0].id,
                'assetUrl': listingAssetCreateResponse.responseData.listingAssets.screenshots[0].primaryAssetUploadUrl
            }]
        }
    };
    var assetCommitResponse = await client.callStoreAPI(client.listingAssetsCommitUrlTemplate, 'put', assetCommitRequest);
    print(assetCommitResponse);

    print('Getting Current Application Draft Status before Submission');
    currentDraftStatus = await client.callStoreAPI(client.productDraftStatusPollingUrlTemplate, 'get');
    print(currentDraftStatus);
    if(!currentDraftStatus.responseData.isReady){
        throw new Error('Application Current Status is not in Ready Status for All Modules');
    }

    print('Creating Submission');
    var submissionCreationResponse = await client.callStoreAPI(client.createSubmissionUrlTemplate, 'post');
    print(submissionCreationResponse);

    print('Current Submission Status');
    var submissionStatusUrl = `${client.submissionStatusPollingUrlTemplate}`.replace('{submissionId}', submissionCreationResponse.responseData.submissionId);
    var submissionStatusResponse = await client.callStoreAPI(submissionStatusUrl, 'get');
    print(submissionStatusResponse);

    // User can Poll on this API to know if Submission Status is INPROGRESS, PUBLISHED or FAILED.
    // This Process involves File Scanning, App Certification and Publishing and can take more than a day.
}

/**
 * Utility Method to Poll using a given function and time interval in seconds
 * @param {*} func 
 * @param {*} intervalInSeconds 
 * @returns 
 */
async function poll(func, intervalInSeconds){
var result = await func();
if(result.responseData.isReady){
    Promise.resolve(true);
}
else if(result.errors && result.errors.length > 0 && result.errors.find(element => element.code == 'packageuploaderror') != undefined){
throw new Error('Package Upload Failed');
}
else{
    await new Promise(resolve => setTimeout(resolve, intervalInSeconds*1000));
    return await poll(func, intervalInSeconds); 
}
}

/**
 * Utility function to Print a Json or normal string
 * @param {*} json 
 */
function print(json){
    if(typeof(json) == 'string'){
        console.log(json);
    }
    else{
        console.log(JSON.stringify(json));
    }
    console.log("\n");
}

/** Run the Node.js Sample Application */
RunNodeJsSample();

Auxiliar de configuração do cliente

O aplicativo de exemplo usa a classe auxiliar ClientConfiguration para passar dados do Microsoft Entra ID Directory e dados do aplicativo para cada um dos métodos de exemplo que usam a API de envio da Microsoft Store.

/** Configuration Object for Store Submission API */
var config = {
    version : "1",
    applicationId : "...",
    clientId : "...",
    clientSecret : "...",
    serviceEndpoint : "https://api.store.microsoft.com",
    tokenEndpoint : "...",
    scope : "https://api.store.microsoft.com/.default",
    sellerId : "...",
    jsonContentType : "application/json",
    pngContentType : "image/png",
    binaryStreamContentType : "application/octet-stream"
};

module.exports = config;

IngestionClient helper usando node.js

A classe IngestionClient fornece métodos auxiliares que são usados por outros métodos no aplicativo de exemplo para executar as seguintes tarefas:

  • Obtenha um token de acesso do Microsoft Entra ID que pode ser usado para invocar métodos na API de submissão da Microsoft Store. Depois de obter um token, você tem 60 minutos para usá-lo em chamadas para a API de envio da Microsoft Store antes que o token expire. Depois que o token expirar, você poderá gerar um novo token.
  • Processe as solicitações HTTP para a API de envio da Microsoft Store.
const fetch = require('node-fetch');
/**
 * Submission Client to invoke all available Store Submission API and Asset Upload to Blob Store
 */
class SubmissionClient{

    constructor(config){
        this.configuration = config;
        this.accessToken = "";
        this.packagesUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/packages`;
        this.packageByIdUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/packages/{packageId}`;
        this.packagesCommitUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/packages/commit`;
        this.appMetadataUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/metadata`;
        this.appListingsFetchMetadataUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/metadata/listings`;
        this.listingAssetsUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/listings/assets`;
        this.listingAssetsCreateUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/listings/assets/create`;
        this.listingAssetsCommitUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/listings/assets/commit`;
        this.productDraftStatusPollingUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/status`;
        this.createSubmissionUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/submit`;
        this.submissionStatusPollingUrlTemplate = `/submission/v${this.configuration.version}/product/${this.configuration.applicationId}/submission/{submissionId}/status`;
    }
    
    async getAccessToken(){
        var params = new URLSearchParams();
        params.append('grant_type','client_credentials');
        params.append('client_id',this.configuration.clientId);
        params.append('client_secret',this.configuration.clientSecret);
        params.append('scope',this.configuration.scope);
        var response = await fetch(this.configuration.tokenEndpoint,{
            method: "POST",
            body: params
        });    
        var data = await response.json();
        this.accessToken = data.access_token;
    }

    async callStoreAPI(url, method, data){
        var request = {
            method: method,
            headers:{
                'Authorization': `Bearer ${this.accessToken}`,
                'Content-Type': this.configuration.jsonContentType,
                'X-Seller-Account-Id': this.configuration.sellerId
            },            
        };
        if(data){
            request.body = JSON.stringify(data);
        }
        var response = await fetch(`${this.configuration.serviceEndpoint}${url}`,request);
        var jsonResponse = await response.json();
        return jsonResponse;
    }

    async uploadAssets(url, stream, size){
        var request = {
            method: 'put',
            headers:{
                'Content-Type': this.configuration.pngContentType,
                'x-ms-blob-type': 'BlockBlob',
                "Content-length": size
            },            
            body: stream
        };
        var response = await fetch(`${url}`,request);
        if(response.ok){
            return response;
        }
        else{
            throw new Error('Uploading of assets failed');
        }
    }
}
module.exports = SubmissionClient;

Ajuda adicional

Se você tiver dúvidas sobre a API de envio da Microsoft Store ou precisar de ajuda para gerenciar seus envios com essa API, use os seguintes recursos:

  • Faça as suas perguntas nos fóruns.
  • Visite nossa página de suporte e solicite uma das opções de suporte assistido para o Partner Center. Se você for solicitado a escolher um tipo e categoria de problema, escolha Envio e certificação de aplicativos e Enviar um aplicativo, respectivamente.

Perguntas frequentes

  1. O que posso fazer através da API de submissão da Microsoft Store?

    A API de envio da Microsoft Store permite que os desenvolvedores automatizem tarefas de gerenciamento de aplicativos, incluindo:

    • Criação e envio de novas atualizações de aplicativos.
    • Carregar e gerir pacotes de aplicações.
    • Atualização de metadados e listagens da Loja.

    Você pode integrar essa API em sua integração contínua ou criar fluxos de trabalho, automatizando tarefas de rotina para simplificar seu processo de publicação.